Compare commits
252 Commits
vertex_spi
...
vulkan-2
Author | SHA1 | Date | |
---|---|---|---|
c9942b35bb | |||
ffe6904502 | |||
fcf9d29a62 | |||
d9d0fc63ec | |||
44097c2a8d | |||
9d7e68b7fc | |||
fdb7ab47ff | |||
0a976f30c1 | |||
8e05af4389 | |||
5317c00c45 | |||
c7f8bc5582 | |||
d2b719ac5a | |||
4accbeba0d | |||
54385a54eb | |||
343717e683 | |||
8d563d37b4 | |||
8c12eb4905 | |||
f1c282775d | |||
0c3fe272b6 | |||
8434d30768 | |||
fbf53686c3 | |||
794d051f0c | |||
3fb48716c5 | |||
359a1b3296 | |||
a2fd43deab | |||
27c280534d | |||
e18e30a8cc | |||
6fbc54b0c5 | |||
49acfe428a | |||
ee26b5b82f | |||
aa8df317af | |||
2cbf6fbb17 | |||
12a5265db1 | |||
eb8b463ca8 | |||
72c1785bf0 | |||
a2aca3dde6 | |||
27be16ee31 | |||
c96f54f022 | |||
d2caf2d386 | |||
1cca713e3b | |||
c5f2267306 | |||
976995ba08 | |||
911fe5610c | |||
b3e0078041 | |||
eeb1ff7965 | |||
07e02a1acf | |||
8721456944 | |||
a23fbae391 | |||
0a8f11ca63 | |||
8f194b5fff | |||
3b050668bb | |||
d054eea0c4 | |||
b9021ea469 | |||
223627c381 | |||
ad4339464a | |||
32cb44d2b9 | |||
39edca2cf7 | |||
0a3acc25d2 | |||
3f0bcf5913 | |||
2384c8f811 | |||
c26cb68a0c | |||
455a0198d9 | |||
c34bc45bf1 | |||
19617f32c8 | |||
8396ce0b47 | |||
07f85cf639 | |||
dfd8ded206 | |||
06caa535d6 | |||
42bed30b98 | |||
74e75f1996 | |||
f04a6a4d83 | |||
33be1b744b | |||
2c9e0ec723 | |||
d1ac33b18b | |||
c00bdc4214 | |||
74be64b60a | |||
11ca327951 | |||
631da59392 | |||
e861c456c9 | |||
c3ab060576 | |||
94ee7c68fc | |||
8e8097e7c0 | |||
52683adedd | |||
e47c47245f | |||
004b83c978 | |||
4bff44c9a9 | |||
18ff007ff2 | |||
e41ceb3c88 | |||
14880862bc | |||
e651bb41bc | |||
8e93039ef9 | |||
65b02f35c7 | |||
d42ccb387a | |||
ee0a7476b3 | |||
45ffc56733 | |||
be8ee3d706 | |||
ab688170f0 | |||
274f6093f3 | |||
9662425337 | |||
6fde9b3354 | |||
e5907f0979 | |||
8f2a5374c3 | |||
c961ecb9a4 | |||
c6f9fd3b65 | |||
3c15398f9e | |||
2855d30815 | |||
8b116aaa04 | |||
cc5ea21f1c | |||
546c7b3bfd | |||
286f750c6c | |||
d8c9335ef0 | |||
cda358443f | |||
bf73cb57ca | |||
9eb1cd2875 | |||
bb8dde8480 | |||
41c10cd5a7 | |||
5aa80873e2 | |||
5215468ff6 | |||
68162c29b4 | |||
a8e4e11cd5 | |||
ab8d1c7d8b | |||
1ab9b60a60 | |||
c2903a6b9d | |||
849d795f0e | |||
bc77e16653 | |||
6ba83db6dd | |||
96effa46e4 | |||
e7a1318773 | |||
ffc9e34281 | |||
20fc09df13 | |||
6c78abb015 | |||
0d3434734a | |||
42a6f7a42e | |||
a40cde7f76 | |||
95e6428d33 | |||
d9ed4600ca | |||
612647f94f | |||
5eb72e9489 | |||
6bef34852c | |||
691cb43871 | |||
6a16a8f60d | |||
1ffd9f08af | |||
c855d84492 | |||
d461778296 | |||
3fe0130fdb | |||
b0880f0ef8 | |||
0bb2e8c618 | |||
84ccb45c5c | |||
f8b853d001 | |||
61e0725d9d | |||
2b9ab33af3 | |||
09350f71e8 | |||
582c438441 | |||
bc10681156 | |||
faefc5cfe1 | |||
f66d03dd48 | |||
fd09f1471e | |||
c1c68a3487 | |||
5849d29fc1 | |||
b31b6e35a2 | |||
79a53c8003 | |||
fd9525acc2 | |||
9b49d94644 | |||
78cb48b23c | |||
3a6a17c708 | |||
938ec204f5 | |||
3d3dd2d162 | |||
7c8bfbb078 | |||
d7bf139e85 | |||
df7f1b13cb | |||
e08e644e73 | |||
ccf36b5e25 | |||
b6427d0ee0 | |||
5c401b8ea0 | |||
bd3571db5a | |||
b18710e4df | |||
99c3eb21b0 | |||
bb8cb3ff82 | |||
69b66cb41d | |||
0f6478a928 | |||
a5f86e9813 | |||
920492925c | |||
0381081c5d | |||
382b64d3c0 | |||
a629aa0dde | |||
b29f263e1b | |||
48d3093fc8 | |||
f77491196c | |||
0fce3e556f | |||
12e69913c2 | |||
89217f8c4b | |||
247b6900c7 | |||
84e54a52a6 | |||
d704c6a3ac | |||
1e42a40640 | |||
7eab7b4151 | |||
e9ccd51286 | |||
9f385ddeb7 | |||
ab73566acc | |||
1e3971038f | |||
306943532c | |||
27698b0c93 | |||
d6cab3ab40 | |||
cbd9a6ffe3 | |||
4752818920 | |||
9b20bcea0f | |||
131129062b | |||
fad1ee7140 | |||
b9ca7bef3f | |||
0e0771ad74 | |||
9c6035f254 | |||
a298e4969b | |||
bd1cabce86 | |||
489248e77f | |||
f593268476 | |||
8fcc3d2121 | |||
a3a8964d46 | |||
c8d614139c | |||
25fe723fa6 | |||
2335b47f4b | |||
10a7b60b12 | |||
98ab3c9610 | |||
0ca25b64e1 | |||
694e49b857 | |||
381db8452b | |||
e2076f2385 | |||
ad2cbe2b26 | |||
9c206db630 | |||
447f29285f | |||
26a9002d97 | |||
a12748d79e | |||
5abfdff66a | |||
8a315d0c8f | |||
4ee36e05b6 | |||
e60a816d89 | |||
5d0e60a6aa | |||
a8848cce43 | |||
11061f36e6 | |||
6d286d5f8c | |||
0e325255f3 | |||
21fe65c29c | |||
4619ed086c | |||
3843122cf8 | |||
d320eef663 | |||
a7a9b94a30 | |||
a9f2a69487 | |||
78965ba18d | |||
52c41d185b | |||
c5c041de89 | |||
f5ed7c775d | |||
e46a88f24a | |||
f18437e59f |
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
|
||||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||||
# ccache configurations
|
# ccache configurations
|
||||||
export CCACHE_CPP2=yes
|
export CCACHE_CPP2=yes
|
||||||
@ -16,16 +15,22 @@ export ASM="clang"
|
|||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
# TODO: LibreSSL ASM disabled due to platform detection issues in build.
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_OSX_ARCHITECTURES="$TARGET_ARCH" \
|
||||||
-DENABLE_QT_TRANSLATION=ON \
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
-DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
-DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||||
-DUSE_DISCORD_PRESENCE=ON \
|
-DUSE_DISCORD_PRESENCE=ON \
|
||||||
-DENABLE_FFMPEG_AUDIO_DECODER=ON \
|
-DENABLE_FFMPEG_AUDIO_DECODER=ON \
|
||||||
-DENABLE_FFMPEG_VIDEO_DUMPER=ON \
|
-DENABLE_FFMPEG_VIDEO_DUMPER=ON \
|
||||||
|
-DENABLE_ASM=OFF \
|
||||||
-GNinja
|
-GNinja
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
ctest -VV -C Release
|
CURRENT_ARCH=`arch`
|
||||||
|
if [ "$TARGET_ARCH" = "$CURRENT_ARCH" ]; then
|
||||||
|
ctest -VV -C Release
|
||||||
|
fi
|
||||||
|
@ -1,23 +1,27 @@
|
|||||||
#!/bin/sh -ex
|
#!/bin/sh -ex
|
||||||
|
|
||||||
brew update
|
brew install ccache ninja || true
|
||||||
brew unlink python@2 || true
|
|
||||||
rm '/usr/local/bin/2to3' || true
|
|
||||||
brew install qt5 molten-vk glslang vulkan-loader p7zip ccache ninja || true
|
|
||||||
pip3 install macpack
|
pip3 install macpack
|
||||||
|
|
||||||
export SDL_VER=2.0.16
|
export FFMPEG_VER=5.1
|
||||||
export FFMPEG_VER=4.4
|
export QT_VER=5.15.8
|
||||||
|
export VULKAN_SDK_VER=1.3.236.0
|
||||||
|
|
||||||
mkdir tmp
|
mkdir tmp
|
||||||
cd tmp/
|
cd tmp/
|
||||||
|
|
||||||
# install SDL
|
|
||||||
wget https://github.com/SachinVin/ext-macos-bin/raw/main/sdl2/sdl-${SDL_VER}.7z
|
|
||||||
7z x sdl-${SDL_VER}.7z
|
|
||||||
cp -rv $(pwd)/sdl-${SDL_VER}/* /
|
|
||||||
|
|
||||||
# install FFMPEG
|
# install FFMPEG
|
||||||
wget https://github.com/SachinVin/ext-macos-bin/raw/main/ffmpeg/ffmpeg-${FFMPEG_VER}.7z
|
wget https://github.com/SachinVin/ext-macos-bin/raw/main/ffmpeg/ffmpeg-${FFMPEG_VER}.7z
|
||||||
7z x ffmpeg-${FFMPEG_VER}.7z
|
7z x ffmpeg-${FFMPEG_VER}.7z
|
||||||
cp -rv $(pwd)/ffmpeg-${FFMPEG_VER}/* /
|
cp -rv $(pwd)/ffmpeg-${FFMPEG_VER}/* /
|
||||||
|
|
||||||
|
# install Qt
|
||||||
|
wget https://github.com/SachinVin/ext-macos-bin/raw/main/qt/qt-${QT_VER}.7z
|
||||||
|
7z x qt-${QT_VER}.7z
|
||||||
|
sudo cp -rv $(pwd)/qt-${QT_VER}/* /usr/local/
|
||||||
|
|
||||||
|
# install Vulkan SDK
|
||||||
|
wget https://sdk.lunarg.com/sdk/download/1.3.236.0/mac/vulkansdk-macos-${VULKAN_SDK_VER}.dmg
|
||||||
|
hdiutil attach vulkansdk-macos-${VULKAN_SDK_VER}.dmg
|
||||||
|
sudo /Volumes/vulkansdk-macos-${VULKAN_SDK_VER}/InstallVulkan.app/Contents/MacOS/InstallVulkan install --accept-licenses --confirm-command --default-answer com.lunarg.vulkan.core com.lunarg.vulkan.usr
|
||||||
|
hdiutil detach /Volumes/vulkansdk-macos-${VULKAN_SDK_VER}
|
||||||
|
45
.ci/macos/universal.sh
Executable file
45
.ci/macos/universal.sh
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
. .ci/common/pre-upload.sh
|
||||||
|
|
||||||
|
REV_NAME="citra-osx-${GITDATE}-${GITREV}"
|
||||||
|
ARCHIVE_NAME="${REV_NAME}.tar.gz"
|
||||||
|
COMPRESSION_FLAGS="-czvf"
|
||||||
|
|
||||||
|
ARTIFACTS_LIST=($ARTIFACTS)
|
||||||
|
|
||||||
|
# Set up the base artifact to combine into.
|
||||||
|
BASE_ARTIFACT=${ARTIFACTS_LIST[0]}
|
||||||
|
BASE_ARTIFACT_ARCH="${BASE_ARTIFACT##*-}"
|
||||||
|
tar xf $BASE_ARTIFACT/$REV_NAME.tar.gz -C $BASE_ARTIFACT
|
||||||
|
mv $BASE_ARTIFACT/$REV_NAME $REV_NAME
|
||||||
|
|
||||||
|
# Executable binary paths that need to be combined.
|
||||||
|
BIN_PATHS=(citra citra-room citra-qt.app/Contents/MacOS/citra-qt)
|
||||||
|
|
||||||
|
# Dylib paths that need to be combined.
|
||||||
|
IFS=$'\n'
|
||||||
|
DYLIB_PATHS=($(cd $REV_NAME && find . -name '*.dylib'))
|
||||||
|
unset IFS
|
||||||
|
|
||||||
|
# Combine all of the executable binaries and dylibs.
|
||||||
|
for OTHER_ARTIFACT in "${ARTIFACTS_LIST[@]:1}"; do
|
||||||
|
OTHER_ARTIFACT_ARCH="${OTHER_ARTIFACT##*-}"
|
||||||
|
|
||||||
|
tar xf $OTHER_ARTIFACT/$REV_NAME.tar.gz -C $OTHER_ARTIFACT
|
||||||
|
|
||||||
|
for BIN_PATH in "${BIN_PATHS[@]}"; do
|
||||||
|
lipo -create -output $REV_NAME/$BIN_PATH $REV_NAME/$BIN_PATH $OTHER_ARTIFACT/$REV_NAME/$BIN_PATH
|
||||||
|
done
|
||||||
|
|
||||||
|
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
|
||||||
|
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
|
||||||
|
DYLIB_INFO=`file $REV_NAME/$DYLIB_PATH`
|
||||||
|
OTHER_DYLIB_INFO=`file $OTHER_ARTIFACT/$REV_NAME/$DYLIB_PATH`
|
||||||
|
if ! [[ "$DYLIB_INFO" =~ "$OTHER_ARTIFACT_ARCH" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "$BASE_ARTIFACT_ARCH" ]]; then
|
||||||
|
lipo -create -output $REV_NAME/$DYLIB_PATH $REV_NAME/$DYLIB_PATH $OTHER_ARTIFACT/$REV_NAME/$DYLIB_PATH
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
. .ci/common/post-upload.sh
|
@ -15,7 +15,7 @@ cp build/bin/Release/citra-room "$REV_NAME"
|
|||||||
BUNDLE_PATH="$REV_NAME/citra-qt.app"
|
BUNDLE_PATH="$REV_NAME/citra-qt.app"
|
||||||
BUNDLE_CONTENTS_PATH="$BUNDLE_PATH/Contents"
|
BUNDLE_CONTENTS_PATH="$BUNDLE_PATH/Contents"
|
||||||
BUNDLE_EXECUTABLE_PATH="$BUNDLE_CONTENTS_PATH/MacOS/citra-qt"
|
BUNDLE_EXECUTABLE_PATH="$BUNDLE_CONTENTS_PATH/MacOS/citra-qt"
|
||||||
BUNDLE_LIB_PATH="$BUNDLE_CONTENTS_PATH/lib"
|
BUNDLE_FRAMEWORKS_PATH="$BUNDLE_CONTENTS_PATH/Frameworks"
|
||||||
BUNDLE_RESOURCES_PATH="$BUNDLE_CONTENTS_PATH/Resources"
|
BUNDLE_RESOURCES_PATH="$BUNDLE_CONTENTS_PATH/Resources"
|
||||||
|
|
||||||
CITRA_STANDALONE_PATH="$REV_NAME/citra"
|
CITRA_STANDALONE_PATH="$REV_NAME/citra"
|
||||||
@ -23,19 +23,19 @@ CITRA_STANDALONE_PATH="$REV_NAME/citra"
|
|||||||
# move libs into folder for deployment
|
# move libs into folder for deployment
|
||||||
macpack $BUNDLE_EXECUTABLE_PATH -d "../Frameworks"
|
macpack $BUNDLE_EXECUTABLE_PATH -d "../Frameworks"
|
||||||
# move qt frameworks into app bundle for deployment
|
# move qt frameworks into app bundle for deployment
|
||||||
$(brew --prefix)/opt/qt5/bin/macdeployqt $BUNDLE_PATH -executable=$BUNDLE_EXECUTABLE_PATH
|
macdeployqt $BUNDLE_PATH -executable=$BUNDLE_EXECUTABLE_PATH
|
||||||
|
|
||||||
# move libs into folder for deployment
|
# move libs into folder for deployment
|
||||||
macpack $CITRA_STANDALONE_PATH -d "libs"
|
macpack $CITRA_STANDALONE_PATH -d "libs"
|
||||||
|
|
||||||
# bundle MoltenVK
|
# bundle MoltenVK
|
||||||
VULKAN_LOADER_PATH=$(brew --prefix vulkan-loader)
|
VULKAN_SDK_PATH=~/VulkanSDK/*/macOS
|
||||||
MOLTENVK_PATH=$(brew --prefix molten-vk)
|
cp $VULKAN_SDK_PATH/lib/libvulkan.dylib $BUNDLE_FRAMEWORKS_PATH
|
||||||
mkdir $BUNDLE_LIB_PATH
|
cp $VULKAN_SDK_PATH/lib/libMoltenVK.dylib $BUNDLE_FRAMEWORKS_PATH
|
||||||
cp $VULKAN_LOADER_PATH/lib/libvulkan.dylib $BUNDLE_LIB_PATH
|
cp $VULKAN_SDK_PATH/lib/libVkLayer_*.dylib $BUNDLE_FRAMEWORKS_PATH
|
||||||
cp $MOLTENVK_PATH/lib/libMoltenVK.dylib $BUNDLE_LIB_PATH
|
cp -r $VULKAN_SDK_PATH/share/vulkan $BUNDLE_RESOURCES_PATH
|
||||||
cp -r $MOLTENVK_PATH/share/vulkan $BUNDLE_RESOURCES_PATH
|
find $BUNDLE_RESOURCES_PATH/vulkan -type f -name "*.json" -exec sed -i'' -e 's/..\/..\/..\/lib/..\/..\/..\/Frameworks/g' {} \;
|
||||||
install_name_tool -add_rpath "@loader_path/../lib/" $BUNDLE_EXECUTABLE_PATH
|
install_name_tool -add_rpath "@loader_path/../Frameworks/" $BUNDLE_EXECUTABLE_PATH
|
||||||
|
|
||||||
# workaround for libc++
|
# workaround for libc++
|
||||||
install_name_tool -change @loader_path/../Frameworks/libc++.1.0.dylib /usr/lib/libc++.1.dylib $BUNDLE_EXECUTABLE_PATH
|
install_name_tool -change @loader_path/../Frameworks/libc++.1.0.dylib /usr/lib/libc++.1.dylib $BUNDLE_EXECUTABLE_PATH
|
||||||
|
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
name: New Issue (Developers Only)
|
||||||
|
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Issue"
|
@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug Report / Feature Request
|
|
||||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Citra or you are requesting a feature you believe would make Citra better.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!---
|
|
||||||
|
|
||||||
Please read the FAQ:
|
|
||||||
https://citra-emu.org/wiki/faq/
|
|
||||||
|
|
||||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
|
||||||
https://community.citra-emu.org/
|
|
||||||
|
|
||||||
If the FAQ does not answer your question, please go to:
|
|
||||||
https://community.citra-emu.org/
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
When submitting an issue, please check the following:
|
|
||||||
|
|
||||||
- You have read the above.
|
|
||||||
- You have provided the version (commit hash) of Citra you are using.
|
|
||||||
- You have provided sufficient detail for the issue to be reproduced.
|
|
||||||
- You have provided system specs (if relevant).
|
|
||||||
- Please also provide:
|
|
||||||
- For any issues, a log file
|
|
||||||
- For crashes, a backtrace.
|
|
||||||
- For graphical issues, comparison screenshots with real hardware.
|
|
||||||
- For emulation inaccuracies, a test-case (if able).
|
|
||||||
|
|
||||||
--->
|
|
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Citra.
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there an existing issue for this?
|
||||||
|
description: Please search to see if an issue already exists for the bug you encountered.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Affected Build(s)
|
||||||
|
description: List the affected build(s) that this issue applies to.
|
||||||
|
placeholder: Nightly 1234 / Canary 1234
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: issue-desc
|
||||||
|
attributes:
|
||||||
|
label: Description of Issue
|
||||||
|
description: A brief description of the issue encountered along with any images and/or videos.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A brief description of how it is expected to work along with any images and/or videos.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction-steps
|
||||||
|
attributes:
|
||||||
|
label: Reproduction Steps
|
||||||
|
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Log File
|
||||||
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: system-config
|
||||||
|
attributes:
|
||||||
|
label: System Configuration
|
||||||
|
placeholder: |
|
||||||
|
CPU: Intel i5-10400 / AMD Ryzen 5 3600
|
||||||
|
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
|
||||||
|
RAM: 16GB DDR4-3200
|
||||||
|
OS: Windows 11 22H2 (Build 22621.819)
|
||||||
|
value: |
|
||||||
|
CPU:
|
||||||
|
GPU/Driver:
|
||||||
|
RAM:
|
||||||
|
OS:
|
||||||
|
validations:
|
||||||
|
required: true
|
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -6,6 +6,3 @@ contact_links:
|
|||||||
- name: Community forums
|
- name: Community forums
|
||||||
url: https://community.citra-emu.org
|
url: https://community.citra-emu.org
|
||||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||||
- name: Citra Android
|
|
||||||
url: https://github.com/citra-emu/citra-android
|
|
||||||
about: If you need tech support on Citra Android, you should use either of the above two options. If you want to file an issue, you should go to the Android repo linked here.
|
|
||||||
|
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: File a feature request
|
||||||
|
labels: "request"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make Citra better.
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there an existing issue for this?
|
||||||
|
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-feature
|
||||||
|
attributes:
|
||||||
|
label: What feature are you suggesting?
|
||||||
|
description: A brief description of the requested feature.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: why-feature
|
||||||
|
attributes:
|
||||||
|
label: Why would this feature be useful?
|
||||||
|
description: A brief description of why this feature would make Citra better.
|
||||||
|
validations:
|
||||||
|
required: true
|
52
.github/workflows/ci.yml
vendored
52
.github/workflows/ci.yml
vendored
@ -74,6 +74,9 @@ jobs:
|
|||||||
path: artifacts/
|
path: artifacts/
|
||||||
macos:
|
macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: ["x86_64", "arm64"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -82,9 +85,9 @@ jobs:
|
|||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~/Library/Caches/ccache
|
path: ~/Library/Caches/ccache
|
||||||
key: ${{ runner.os }}-macos-${{ github.sha }}
|
key: ${{ runner.os }}-macos-${{ matrix.arch }}-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-macos-
|
${{ runner.os }}-macos-${{ matrix.arch }}-
|
||||||
- name: Query tag name
|
- name: Query tag name
|
||||||
uses: little-core-labs/get-git-tag@v3.0.2
|
uses: little-core-labs/get-git-tag@v3.0.2
|
||||||
id: tagName
|
id: tagName
|
||||||
@ -93,15 +96,51 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: ./.ci/macos/build.sh
|
run: ./.ci/macos/build.sh
|
||||||
env:
|
env:
|
||||||
MACOSX_DEPLOYMENT_TARGET: "10.13"
|
MACOSX_DEPLOYMENT_TARGET: "11"
|
||||||
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
||||||
|
TARGET_ARCH: ${{ matrix.arch }}
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/macos/upload.sh
|
run: ./.ci/macos/upload.sh
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: macos-${{ matrix.arch }}
|
||||||
|
path: artifacts/
|
||||||
|
macos-universal:
|
||||||
|
runs-on: macos-latest
|
||||||
|
needs: macos
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Query tag name
|
||||||
|
uses: little-core-labs/get-git-tag@v3.0.2
|
||||||
|
id: tagName
|
||||||
|
- name: Download x86 build
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: macos-x86_64
|
||||||
|
path: macos-x86_64/
|
||||||
|
- name: Download ARM64 build
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: macos-arm64
|
||||||
|
path: macos-arm64/
|
||||||
|
- name: Create universal app
|
||||||
|
run: ./.ci/macos/universal.sh
|
||||||
|
env:
|
||||||
|
ARTIFACTS: macos-x86_64 macos-arm64
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
- name: Delete intermediate artifacts
|
||||||
|
uses: geekyeggo/delete-artifact@v2
|
||||||
|
with:
|
||||||
|
name: |
|
||||||
|
macos-x86_64
|
||||||
|
macos-arm64
|
||||||
windows:
|
windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
@ -165,7 +204,10 @@ jobs:
|
|||||||
- name: Deps
|
- name: Deps
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install glslang-tools ccache apksigner -y
|
sudo apt-get install ccache apksigner -y
|
||||||
|
sudo add-apt-repository -y ppa:savoury1/ffmpeg4
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install --no-install-recommends -y glslang-dev glslang-tools
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./.ci/android/build.sh
|
run: ./.ci/android/build.sh
|
||||||
- name: Copy and sign artifacts
|
- name: Copy and sign artifacts
|
||||||
@ -193,7 +235,7 @@ jobs:
|
|||||||
TRANSIFEX_API_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }}
|
TRANSIFEX_API_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }}
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build, android, macos, source, windows]
|
needs: [build, android, macos-universal, source, windows]
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
|
37
.gitmodules
vendored
37
.gitmodules
vendored
@ -12,13 +12,10 @@
|
|||||||
url = https://github.com/catchorg/Catch2
|
url = https://github.com/catchorg/Catch2
|
||||||
[submodule "dynarmic"]
|
[submodule "dynarmic"]
|
||||||
path = externals/dynarmic
|
path = externals/dynarmic
|
||||||
url = https://github.com/citra-emu/dynarmic.git
|
url = https://github.com/merryhime/dynarmic.git
|
||||||
[submodule "xbyak"]
|
[submodule "xbyak"]
|
||||||
path = externals/xbyak
|
path = externals/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
url = https://github.com/herumi/xbyak.git
|
||||||
[submodule "cryptopp"]
|
|
||||||
path = externals/cryptopp/cryptopp
|
|
||||||
url = https://github.com/weidai11/cryptopp.git
|
|
||||||
[submodule "fmt"]
|
[submodule "fmt"]
|
||||||
path = externals/fmt
|
path = externals/fmt
|
||||||
url = https://github.com/fmtlib/fmt.git
|
url = https://github.com/fmtlib/fmt.git
|
||||||
@ -46,9 +43,6 @@
|
|||||||
[submodule "teakra"]
|
[submodule "teakra"]
|
||||||
path = externals/teakra
|
path = externals/teakra
|
||||||
url = https://github.com/wwylele/teakra.git
|
url = https://github.com/wwylele/teakra.git
|
||||||
[submodule "lodepng"]
|
|
||||||
path = externals/lodepng/lodepng
|
|
||||||
url = https://github.com/lvandeve/lodepng.git
|
|
||||||
[submodule "zstd"]
|
[submodule "zstd"]
|
||||||
path = externals/zstd
|
path = externals/zstd
|
||||||
url = https://github.com/facebook/zstd.git
|
url = https://github.com/facebook/zstd.git
|
||||||
@ -56,17 +50,26 @@
|
|||||||
path = externals/libyuv
|
path = externals/libyuv
|
||||||
url = https://github.com/lemenkov/libyuv.git
|
url = https://github.com/lemenkov/libyuv.git
|
||||||
[submodule "sdl2"]
|
[submodule "sdl2"]
|
||||||
path = externals/sdl2/SDL
|
path = externals/sdl2/SDL
|
||||||
url = https://github.com/libsdl-org/SDL
|
url = https://github.com/libsdl-org/SDL
|
||||||
|
[submodule "cryptopp-cmake"]
|
||||||
|
path = externals/cryptopp-cmake
|
||||||
|
url = https://github.com/abdes/cryptopp-cmake.git
|
||||||
|
[submodule "cryptopp"]
|
||||||
|
path = externals/cryptopp
|
||||||
|
url = https://github.com/weidai11/cryptopp.git
|
||||||
[submodule "vulkan-headers"]
|
[submodule "vulkan-headers"]
|
||||||
path = externals/vulkan-headers
|
path = externals/vulkan-headers
|
||||||
url = https://github.com/KhronosGroup/Vulkan-Headers
|
url = https://github.com/KhronosGroup/Vulkan-Headers
|
||||||
[submodule "glslang"]
|
[submodule "glslang"]
|
||||||
path = externals/glslang
|
path = externals/glslang
|
||||||
url = https://github.com/KhronosGroup/glslang
|
url = https://github.com/KhronosGroup/glslang
|
||||||
[submodule "glm"]
|
[submodule "glm"]
|
||||||
path = externals/glm
|
path = externals/glm
|
||||||
url = https://github.com/g-truc/glm
|
url = https://github.com/g-truc/glm
|
||||||
[submodule "sirit"]
|
[submodule "sirit"]
|
||||||
path = externals/sirit
|
path = externals/sirit
|
||||||
url = https://github.com/GPUCode/sirit
|
url = https://github.com/GPUCode/sirit
|
||||||
|
[submodule "zlib-ng"]
|
||||||
|
path = externals/zlib-ng/zlib-ng
|
||||||
|
url = https://github.com/zlib-ng/zlib-ng
|
||||||
|
@ -147,14 +147,14 @@ function(detect_architecture symbol arch)
|
|||||||
# CMake's crazy scope rules will keep it defined
|
# CMake's crazy scope rules will keep it defined
|
||||||
if (ARCHITECTURE_${arch})
|
if (ARCHITECTURE_${arch})
|
||||||
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
|
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
|
||||||
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
|
|
||||||
add_definitions(-DARCHITECTURE_${arch}=1)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
if (NOT ENABLE_GENERIC)
|
if (NOT ENABLE_GENERIC)
|
||||||
if (MSVC)
|
if (CMAKE_OSX_ARCHITECTURES)
|
||||||
|
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
|
||||||
|
elseif (MSVC)
|
||||||
detect_architecture("_M_AMD64" x86_64)
|
detect_architecture("_M_AMD64" x86_64)
|
||||||
detect_architecture("_M_IX86" x86)
|
detect_architecture("_M_IX86" x86)
|
||||||
detect_architecture("_M_ARM" arm)
|
detect_architecture("_M_ARM" arm)
|
||||||
@ -168,8 +168,6 @@ if (NOT ENABLE_GENERIC)
|
|||||||
endif()
|
endif()
|
||||||
if (NOT DEFINED ARCHITECTURE)
|
if (NOT DEFINED ARCHITECTURE)
|
||||||
set(ARCHITECTURE "GENERIC")
|
set(ARCHITECTURE "GENERIC")
|
||||||
set(ARCHITECTURE_GENERIC 1)
|
|
||||||
add_definitions(-DARCHITECTURE_GENERIC=1)
|
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||||
|
|
||||||
@ -195,7 +193,7 @@ find_package(Threads REQUIRED)
|
|||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
if (CITRA_USE_BUNDLED_QT)
|
if (CITRA_USE_BUNDLED_QT)
|
||||||
if (MSVC_VERSION GREATER_EQUAL 1920 AND ARCHITECTURE_x86_64)
|
if (MSVC_VERSION GREATER_EQUAL 1920 AND "x86_64" IN_LIST ARCHITECTURE)
|
||||||
set(QT_VER qt-5.15.7-msvc2019_64)
|
set(QT_VER qt-5.15.7-msvc2019_64)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
|
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
|
||||||
@ -212,7 +210,11 @@ if (ENABLE_QT)
|
|||||||
set(QT_PREFIX_HINT)
|
set(QT_PREFIX_HINT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia ${QT_PREFIX_HINT})
|
find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia Concurrent ${QT_PREFIX_HINT})
|
||||||
|
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
find_package(Qt5 REQUIRED COMPONENTS DBus ${QT_PREFIX_HINT})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
||||||
@ -232,7 +234,7 @@ endif()
|
|||||||
|
|
||||||
if (ENABLE_FFMPEG)
|
if (ENABLE_FFMPEG)
|
||||||
if (CITRA_USE_BUNDLED_FFMPEG)
|
if (CITRA_USE_BUNDLED_FFMPEG)
|
||||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND "x86_64" IN_LIST ARCHITECTURE)
|
||||||
set(FFmpeg_VER "ffmpeg-4.1-win64")
|
set(FFmpeg_VER "ffmpeg-4.1-win64")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "No bundled FFmpeg binaries for your toolchain. Disable CITRA_USE_BUNDLED_FFMPEG and provide your own.")
|
message(FATAL_ERROR "No bundled FFmpeg binaries for your toolchain. Disable CITRA_USE_BUNDLED_FFMPEG and provide your own.")
|
||||||
@ -382,12 +384,18 @@ endif()
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
|
|
||||||
|
# See externals/CMakeLists.txt
|
||||||
|
foreach(def ${CRYPTOPP_COMPILE_DEFINITIONS})
|
||||||
|
add_definitions(-D${def})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if (USE_SYSTEM_BOOST)
|
if (USE_SYSTEM_BOOST)
|
||||||
find_package(Boost 1.70.0 COMPONENTS serialization REQUIRED)
|
find_package(Boost 1.70.0 COMPONENTS serialization iostreams REQUIRED)
|
||||||
else()
|
else()
|
||||||
add_library(Boost::boost ALIAS boost)
|
add_library(Boost::boost ALIAS boost)
|
||||||
add_library(Boost::serialization ALIAS boost_serialization)
|
add_library(Boost::serialization ALIAS boost_serialization)
|
||||||
|
add_library(Boost::iostreams ALIAS boost_iostreams)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
|
@ -17,6 +17,7 @@ function(copy_citra_Qt5_deps target_dir)
|
|||||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||||
|
Qt5Concurrent$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Network$<$<CONFIG:Debug>:d>.*
|
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||||
)
|
)
|
||||||
|
79
README.md
79
README.md
@ -1,24 +1,49 @@
|
|||||||
**BEFORE FILING AN ISSUE, READ THE RELEVANT SECTION IN THE [CONTRIBUTING](https://github.com/citra-emu/citra/wiki/Contributing#reporting-issues) FILE!!!**
|
<h1 align="center">
|
||||||
|
<br>
|
||||||
|
<a href="https://citra-emu.org/"><img src="https://raw.githubusercontent.com/citra-emu/citra-assets/master/Main/citra_logo.svg" alt="Citra" width="200"></a>
|
||||||
|
<br>
|
||||||
|
<b>Citra</b>
|
||||||
|
<br>
|
||||||
|
</h1>
|
||||||
|
|
||||||
# Citra
|
<h4 align="center"><b>Citra</b> is the world's most popular, open-source, Nintendo 3DS emulator.
|
||||||
|
<br>
|
||||||
|
It is written in C++ with portability in mind and builds are actively maintained for Windows, Linux, Android and macOS.
|
||||||
|
</h4>
|
||||||
|
|
||||||
[](https://github.com/citra-emu/citra/actions)
|
<p align="center">
|
||||||
[](https://app.bitrise.io/app/4ccd8e5720f0d13b)
|
<a href="https://github.com/citra-emu/citra/actions/">
|
||||||
[](https://discord.gg/FAXfZV9)
|
<img src="https://github.com/citra-emu/citra/workflows/citra-ci/badge.svg"
|
||||||
|
alt="GitHub Actions Build Status">
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/FAXfZV9">
|
||||||
|
<img src="https://img.shields.io/discord/220740965957107713?color=%237289DA&label=Citra&logo=discord&logoColor=white"
|
||||||
|
alt="Discord">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS.
|
<p align="center">
|
||||||
|
<a href="#compatibility">Compatibility</a> |
|
||||||
|
<a href="#releases">Releases</a> |
|
||||||
|
<a href="#development">Development</a> |
|
||||||
|
<a href="#building">Building</a> |
|
||||||
|
<a href="#support">Support</a> |
|
||||||
|
<a href="#license">License</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
Citra emulates a subset of 3DS hardware and therefore is useful for running/debugging homebrew applications, and it is also able to run many commercial games! Some of these do not run at a playable state, but we are working every day to advance the project forward. (Playable here means compatibility of at least "Okay" on our [game compatibility list](https://citra-emu.org/game).)
|
|
||||||
|
|
||||||
Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project.
|
## Compatibility
|
||||||
|
|
||||||
Check out our [website](https://citra-emu.org/)!
|
The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.
|
||||||
|
|
||||||
|
For a full list of games Citra supports, please visit our [Compatibility page](https://citra-emu.org/game/)
|
||||||
|
|
||||||
|
Check out our [website](https://citra-emu.org/) for the latest news on exciting features, progress reports, and more!
|
||||||
|
Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project.
|
||||||
|
|
||||||
Need help? Check out our [asking for help](https://citra-emu.org/help/reference/asking/) guide.
|
Need help? Check out our [asking for help](https://citra-emu.org/help/reference/asking/) guide.
|
||||||
|
|
||||||
For development discussion, please join us on our [Discord server](https://citra-emu.org/discord/) or at #citra-dev on libera.
|
## Releases
|
||||||
|
|
||||||
### Releases
|
|
||||||
|
|
||||||
Citra has two main release channels: Nightly and Canary.
|
Citra has two main release channels: Nightly and Canary.
|
||||||
|
|
||||||
@ -28,30 +53,46 @@ The [Canary](https://github.com/citra-emu/citra-canary) build is based on the ma
|
|||||||
|
|
||||||
Both builds can be installed with the installer provided on the [website](https://citra-emu.org/download/), but those looking for specific versions or standalone releases can find them in the release tabs of the [Nightly](https://github.com/citra-emu/citra-nightly/releases) and [Canary](https://github.com/citra-emu/citra-canary/releases) repositories.
|
Both builds can be installed with the installer provided on the [website](https://citra-emu.org/download/), but those looking for specific versions or standalone releases can find them in the release tabs of the [Nightly](https://github.com/citra-emu/citra-nightly/releases) and [Canary](https://github.com/citra-emu/citra-canary/releases) repositories.
|
||||||
|
|
||||||
Currently, development and releases of the Android version are in [a separate repository](https://github.com/citra-emu/citra-android).
|
Android builds can be downloaded from the Google Play Store.
|
||||||
|
|
||||||
A Flatpak for Citra is available on [Flathub](https://flathub.org/apps/details/org.citra_emu.citra). Details on the build process can be found in [our Flathub repository](https://github.com/flathub/org.citra_emu.citra).
|
A Flatpak for Citra is available on [Flathub](https://flathub.org/apps/details/org.citra_emu.citra). Details on the build process can be found in [our Flathub repository](https://github.com/flathub/org.citra_emu.citra).
|
||||||
|
|
||||||
### Development
|
## Development
|
||||||
|
|
||||||
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/citra-emu/citra) is hosted.
|
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/citra-emu/citra) is hosted.
|
||||||
|
For development discussion, please join us on our [Discord server](https://citra-emu.org/discord/) or at #citra-dev on libera.
|
||||||
|
|
||||||
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/citra-emu/citra/wiki/Contributing) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You should also contact any of the developers in the forum in order to know about the current state of the emulator because the [TODO list](https://docs.google.com/document/d/1SWIop0uBI9IW8VGg97TAtoT_CHNoP42FzYmvG1F4QDA) isn't maintained anymore.
|
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/citra-emu/citra/wiki/Contributing) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You can also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||||
|
|
||||||
If you want to contribute to the user interface translation, please check out the [citra project on transifex](https://www.transifex.com/citra/citra). We centralize the translation work there, and periodically upstream translations.
|
If you want to contribute to the user interface translation, please check out the [Citra project on transifex](https://www.transifex.com/citra/citra). We centralize the translation work there, and periodically upstream translations.
|
||||||
|
|
||||||
### Building
|
## Building
|
||||||
|
|
||||||
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows)
|
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows)
|
||||||
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux)
|
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux)
|
||||||
* __macOS__: [macOS Build](https://github.com/citra-emu/citra/wiki/Building-for-macOS)
|
* __macOS__: [macOS Build](https://github.com/citra-emu/citra/wiki/Building-for-macOS)
|
||||||
|
* __Android__: [Android Build](https://github.com/citra-emu/citra/wiki/Building-for-Android)
|
||||||
|
|
||||||
|
|
||||||
### Support
|
## Support
|
||||||
We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra. Any donations received will go towards things like:
|
|
||||||
|
If you enjoy the project and want to support us financially, check out our Patreon!
|
||||||
|
|
||||||
|
<a href="https://www.patreon.com/citraemu">
|
||||||
|
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
We also happily accept donated games and hardware.
|
||||||
|
Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra.
|
||||||
|
Any donations received will go towards things like:
|
||||||
* 3DS consoles for developers to explore the hardware
|
* 3DS consoles for developers to explore the hardware
|
||||||
* 3DS games for testing
|
* 3DS games for testing
|
||||||
* Any equipment required for homebrew
|
* Any equipment required for homebrew
|
||||||
* Infrastructure setup
|
* Infrastructure setup
|
||||||
|
|
||||||
We also more than gladly accept used 3DS consoles! If you would like to give yours away, don't hesitate to join our [Discord server](https://citra-emu.org/discord/) and talk to bunnei.
|
We also more than gladly accept used 3DS consoles! If you would like to give yours away, don't hesitate to join our [Discord server](https://citra-emu.org/discord/) and talk to bunnei.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Citra is licensed under the GPLv2 (or any later version). Refer to the [LICENSE.txt](https://github.com/citra-emu/citra/blob/master/license.txt) file.
|
||||||
|
18
dist/languages/.tx/config
vendored
18
dist/languages/.tx/config
vendored
@ -1,9 +1,9 @@
|
|||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
|
|
||||||
[citra.emulator]
|
[o:citra:p:citra:r:emulator]
|
||||||
file_filter = <lang>.ts
|
file_filter = <lang>.ts
|
||||||
source_file = en.ts
|
source_file = en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = QT
|
type = QT
|
||||||
|
|
||||||
|
17
dist/qt_themes/default/style.qss
vendored
17
dist/qt_themes/default/style.qss
vendored
@ -10,4 +10,19 @@ QPushButton#GraphicsAPIStatusBarButton {
|
|||||||
|
|
||||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||||
border: 1px solid #76797C;
|
border: 1px solid #76797C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton {
|
||||||
|
color: #A5A5A5;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0px 3px 0px 3px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 60px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton:hover {
|
||||||
|
border: 1px solid #76797C;
|
||||||
|
}
|
||||||
|
17
dist/qt_themes/qdarkstyle/style.qss
vendored
17
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -1249,4 +1249,19 @@ QPushButton#GraphicsAPIStatusBarButton {
|
|||||||
|
|
||||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||||
border: 1px solid #76797C;
|
border: 1px solid #76797C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton {
|
||||||
|
color: #A5A5A5;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0px 3px 0px 3px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 60px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton:hover {
|
||||||
|
border: 1px solid #76797C;
|
||||||
|
}
|
||||||
|
54
externals/CMakeLists.txt
vendored
54
externals/CMakeLists.txt
vendored
@ -21,6 +21,15 @@ file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/seri
|
|||||||
add_library(boost_serialization STATIC ${boost_serialization_SRC})
|
add_library(boost_serialization STATIC ${boost_serialization_SRC})
|
||||||
target_link_libraries(boost_serialization PUBLIC boost)
|
target_link_libraries(boost_serialization PUBLIC boost)
|
||||||
|
|
||||||
|
# Boost::iostreams
|
||||||
|
add_library(
|
||||||
|
boost_iostreams
|
||||||
|
STATIC
|
||||||
|
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/file_descriptor.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(boost_iostreams PUBLIC boost)
|
||||||
|
|
||||||
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
||||||
|
|
||||||
# Catch2
|
# Catch2
|
||||||
@ -29,25 +38,45 @@ set(CATCH_INSTALL_EXTRAS OFF)
|
|||||||
add_subdirectory(catch2)
|
add_subdirectory(catch2)
|
||||||
|
|
||||||
# Crypto++
|
# Crypto++
|
||||||
add_subdirectory(cryptopp)
|
set(CRYPTOPP_BUILD_DOCUMENTATION OFF)
|
||||||
|
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||||
|
set(CRYPTOPP_INSTALL OFF)
|
||||||
|
set(CRYPTOPP_SOURCES "${CMAKE_SOURCE_DIR}/externals/cryptopp")
|
||||||
|
add_subdirectory(cryptopp-cmake)
|
||||||
|
|
||||||
|
# HACK: Mismatch between compilation of CryptoPP and headers used in Citra can cause runtime issues.
|
||||||
|
# Pull out the compile definitions from CryptoPP and apply them to Citra as well to fix this.
|
||||||
|
# See: https://github.com/weidai11/cryptopp/issues/1191
|
||||||
|
get_source_file_property(CRYPTOPP_COMPILE_DEFINITIONS ${CRYPTOPP_SOURCES}/cryptlib.cpp TARGET_DIRECTORY cryptopp COMPILE_DEFINITIONS)
|
||||||
|
set(CRYPTOPP_COMPILE_DEFINITIONS ${CRYPTOPP_COMPILE_DEFINITIONS} PARENT_SCOPE)
|
||||||
|
|
||||||
|
# HACK: The logic to set up the base include directory for CryptoPP does not work with Android SDK CMake 3.22.1.
|
||||||
|
# Until there is a fixed version available, this code will detect and add in the proper include if it does not exist.
|
||||||
|
if(ANDROID)
|
||||||
|
message(STATUS "Applying CryptoPP include fix.")
|
||||||
|
get_target_property(CRYPTOPP_INCLUDES cryptopp INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
foreach(CRYPTOPP_INCLUDE ${CRYPTOPP_INCLUDES})
|
||||||
|
if("${CRYPTOPP_INCLUDE}" MATCHES "\\$<BUILD_INTERFACE:(.*)/cryptopp>")
|
||||||
|
message(STATUS "Fixed include path: ${CMAKE_MATCH_1}")
|
||||||
|
target_include_directories(cryptopp PUBLIC $<BUILD_INTERFACE:${CMAKE_MATCH_1}>)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
# fmt and Xbyak need to be added before dynarmic
|
# fmt and Xbyak need to be added before dynarmic
|
||||||
# libfmt
|
# libfmt
|
||||||
|
option(FMT_INSTALL "" ON)
|
||||||
add_subdirectory(fmt)
|
add_subdirectory(fmt)
|
||||||
|
|
||||||
# Xbyak
|
# Xbyak
|
||||||
if (ARCHITECTURE_x86_64)
|
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
add_library(xbyak INTERFACE)
|
add_subdirectory(xbyak)
|
||||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
|
||||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
|
||||||
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
|
||||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Dynarmic
|
# Dynarmic
|
||||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
if ("x86_64" IN_LIST ARCHITECTURE OR "arm64" IN_LIST ARCHITECTURE)
|
||||||
set(DYNARMIC_TESTS OFF)
|
set(DYNARMIC_TESTS OFF)
|
||||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
|
||||||
set(DYNARMIC_FRONTENDS "A32")
|
set(DYNARMIC_FRONTENDS "A32")
|
||||||
add_subdirectory(dynarmic)
|
add_subdirectory(dynarmic)
|
||||||
endif()
|
endif()
|
||||||
@ -100,6 +129,9 @@ if (ENABLE_SDL2 AND NOT USE_SYSTEM_SDL2)
|
|||||||
add_subdirectory(sdl2)
|
add_subdirectory(sdl2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Zlib
|
||||||
|
add_subdirectory(zlib-ng)
|
||||||
|
|
||||||
# Zstandard
|
# Zstandard
|
||||||
set(ZSTD_LEGACY_SUPPORT OFF)
|
set(ZSTD_LEGACY_SUPPORT OFF)
|
||||||
set(ZSTD_BUILD_PROGRAMS OFF)
|
set(ZSTD_BUILD_PROGRAMS OFF)
|
||||||
@ -158,8 +190,8 @@ if (ENABLE_WEB_SERVICE)
|
|||||||
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# lodepng
|
# libspng
|
||||||
add_subdirectory(lodepng)
|
add_subdirectory(libspng)
|
||||||
|
|
||||||
# (xperia64): Only use libyuv on Android b/c of build issues on Windows and mandatory JPEG
|
# (xperia64): Only use libyuv on Android b/c of build issues on Windows and mandatory JPEG
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
|
2
externals/boost
vendored
2
externals/boost
vendored
Submodule externals/boost updated: 66937ea62d...80a171a179
1
externals/cryptopp
vendored
Submodule
1
externals/cryptopp
vendored
Submodule
Submodule externals/cryptopp added at 511806c0eb
1
externals/cryptopp-cmake
vendored
Submodule
1
externals/cryptopp-cmake
vendored
Submodule
Submodule externals/cryptopp-cmake added at 15798ac9c2
449
externals/cryptopp/CMakeLists.txt
vendored
449
externals/cryptopp/CMakeLists.txt
vendored
@ -1,449 +0,0 @@
|
|||||||
# A trimmed down version of the CMakeLists.txt from noloader/cryptopp-cmake
|
|
||||||
# The differences are:
|
|
||||||
# - removed support for legacy CMake versions
|
|
||||||
# - removed support for 32-bit
|
|
||||||
# - added prefix "CRYPTOPP_OPT_" to all option names
|
|
||||||
# - disabled testing
|
|
||||||
# - disabled installation
|
|
||||||
# - disabled documentation
|
|
||||||
# - configured to build a static library only
|
|
||||||
# - adds include directories to the library target
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1)
|
|
||||||
if (POLICY CMP0048)
|
|
||||||
cmake_policy(SET CMP0048 NEW)
|
|
||||||
endif ()
|
|
||||||
project(cryptopp VERSION 8.5.0)
|
|
||||||
if (POLICY CMP0054)
|
|
||||||
cmake_policy(SET CMP0054 NEW)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
|
|
||||||
|
|
||||||
include(TestBigEndian)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
include(CheckCXXCompilerFlag)
|
|
||||||
|
|
||||||
set(TEST_PROG_DIR ${SRC_DIR}/TestPrograms)
|
|
||||||
set(TEST_CXX_FILE ${TEST_PROG_DIR}/test_cxx.cxx)
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Settable options
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_ASM "Disable ASM" OFF)
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_SSSE3 "Disable SSSE3" OFF)
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_SSE4 "Disable SSE4" OFF)
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_AESNI "Disable AES-NI" OFF)
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_SHA "Disable SHA" OFF)
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_AVX "Disable AVX" OFF)
|
|
||||||
option(CRYPTOPP_OPT_DISABLE_AVX2 "Disable AVX2" OFF)
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Compiler options
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
# Only set when cross-compiling, http://www.vtk.org/Wiki/CMake_Cross_Compiling
|
|
||||||
if (NOT (CMAKE_SYSTEM_VERSION AND CMAKE_SYSTEM_PROCESSOR))
|
|
||||||
set(CRYPTOPP_CROSS_COMPILE 1)
|
|
||||||
else()
|
|
||||||
set(CRYPTOPP_CROSS_COMPILE 0)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CRYPTOPP_COMPILE_DEFINITIONS)
|
|
||||||
set(CRYPTOPP_COMPILE_OPTIONS)
|
|
||||||
|
|
||||||
# Don't use RPATH's. The resulting binary could fail a security audit.
|
|
||||||
set(CMAKE_MACOSX_RPATH 0)
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS -wd68 -wd186 -wd279 -wd327 -wd161 -wd3180)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
# Disable C4390: empty controlled statement found: is this the intent?
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4390")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Endianness
|
|
||||||
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
|
|
||||||
if(IS_BIG_ENDIAN)
|
|
||||||
add_definitions(-DIS_BIG_ENDIAN)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_ASM)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_ASM)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_SSSE3)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_SSSE3)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_SSE4)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_SSSE4)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_CLMUL)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_CLMUL)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_AESNI)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_AESNI)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_RDRAND)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_RDRAND)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_RDSEED)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_RDSEED)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_AVX)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_AVX)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_AVX2)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_AVX2)
|
|
||||||
endif ()
|
|
||||||
if (CRYPTOPP_OPT_DISABLE_SHA)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS CRYPTOPP_DISABLE_SHA)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# We need the output 'uname -s' for Unix and Linux system detection
|
|
||||||
if (NOT CRYPTOPP_CROSS_COMPILE)
|
|
||||||
set (UNAME_CMD "uname")
|
|
||||||
set (UNAME_ARG "-s")
|
|
||||||
execute_process(COMMAND ${UNAME_CMD} ${UNAME_ARG}
|
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
|
||||||
RESULT_VARIABLE UNAME_RESULT
|
|
||||||
OUTPUT_VARIABLE UNAME_SYSTEM)
|
|
||||||
string(REGEX REPLACE "\n$" "" UNAME_SYSTEM "${UNAME_SYSTEM}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# We need the output 'uname -m' for Unix and Linux platform detection
|
|
||||||
if (NOT CRYPTOPP_CROSS_COMPILE)
|
|
||||||
set (UNAME_CMD "uname")
|
|
||||||
set (UNAME_ARG "-m")
|
|
||||||
execute_process(COMMAND ${UNAME_CMD} ${UNAME_ARG}
|
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
|
||||||
RESULT_VARIABLE UNAME_RESULT
|
|
||||||
OUTPUT_VARIABLE UNAME_MACHINE)
|
|
||||||
string(REGEX REPLACE "\n$" "" UNAME_MACHINE "${UNAME_MACHINE}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
# Try to find a Posix compatible grep and sed. Solaris, Digital Unix,
|
|
||||||
# Tru64, HP-UX and a few others need tweaking
|
|
||||||
|
|
||||||
if (EXISTS /usr/xpg4/bin/grep)
|
|
||||||
set(GREP_CMD /usr/xpg4/bin/grep)
|
|
||||||
elseif (EXISTS /usr/gnu/bin/grep)
|
|
||||||
set(GREP_CMD /usr/gnu/bin/grep)
|
|
||||||
elseif (EXISTS /usr/linux/bin/grep)
|
|
||||||
set(GREP_CMD /usr/linux/bin/grep)
|
|
||||||
else ()
|
|
||||||
set(GREP_CMD grep)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (EXISTS /usr/xpg4/bin/sed)
|
|
||||||
set(SED_CMD /usr/xpg4/bin/sed)
|
|
||||||
elseif (EXISTS /usr/gnu/bin/sed)
|
|
||||||
set(SED_CMD /usr/gnu/bin/sed)
|
|
||||||
elseif (EXISTS /usr/linux/bin/sed)
|
|
||||||
set(SED_CMD /usr/linux/bin/sed)
|
|
||||||
else ()
|
|
||||||
set(SED_CMD sed)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function(CheckCompileOption opt var)
|
|
||||||
CHECK_CXX_COMPILER_FLAG(${opt} ${var})
|
|
||||||
endfunction(CheckCompileOption)
|
|
||||||
|
|
||||||
function(CheckCompileLinkOption opt var prog)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
|
|
||||||
# TODO: improve this...
|
|
||||||
CHECK_CXX_COMPILER_FLAG(${opt} ${var})
|
|
||||||
|
|
||||||
elseif (APPLE)
|
|
||||||
|
|
||||||
message(STATUS "Performing Test ${var}")
|
|
||||||
try_compile(COMMAND_SUCCESS ${CMAKE_BINARY_DIR} ${prog} COMPILE_DEFINITIONS ${opt})
|
|
||||||
if (COMMAND_SUCCESS)
|
|
||||||
set(${var} 1 PARENT_SCOPE)
|
|
||||||
message(STATUS "Performing Test ${var} - Success")
|
|
||||||
else ()
|
|
||||||
set(${var} 0 PARENT_SCOPE)
|
|
||||||
message(STATUS "Performing Test ${var} - Failed")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
else ()
|
|
||||||
|
|
||||||
message(STATUS "Performing Test ${var}")
|
|
||||||
try_compile(COMMAND_SUCCESS ${CMAKE_BINARY_DIR} ${prog} COMPILE_DEFINITIONS ${opt})
|
|
||||||
if (COMMAND_SUCCESS)
|
|
||||||
set(${var} 1 PARENT_SCOPE)
|
|
||||||
message(STATUS "Performing Test ${var} - Success")
|
|
||||||
else ()
|
|
||||||
set(${var} 0 PARENT_SCOPE)
|
|
||||||
message(STATUS "Performing Test ${var} - Failed")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
endfunction(CheckCompileLinkOption)
|
|
||||||
|
|
||||||
function(AddCompileOption opt)
|
|
||||||
|
|
||||||
if ("${COMMAND_OUTPUT}" NOT STREQUAL "")
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "${opt}")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
endfunction(AddCompileOption)
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
function(DumpMachine output pattern)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
|
|
||||||
# CMake does not provide a generic shell/terminal mechanism
|
|
||||||
# and Microsoft environments don't know what 'sh' is.
|
|
||||||
set(${output} 0 PARENT_SCOPE)
|
|
||||||
|
|
||||||
else ()
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES ${pattern})
|
|
||||||
set(${output} TRUE PARENT_SCOPE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endfunction(DumpMachine)
|
|
||||||
|
|
||||||
# Thansk to Anonimal for MinGW; see http://github.com/weidai11/cryptopp/issues/466
|
|
||||||
DumpMachine(CRYPTOPP_AMD64 "(x86_64|AMD64|amd64)")
|
|
||||||
DumpMachine(CRYPTOPP_I386 "(i.86)")
|
|
||||||
DumpMachine(CRYPTOPP_MINGW64 "(w64-mingw32)|(mingw64)")
|
|
||||||
DumpMachine(CRYPTOPP_ARMV8 "(armv8|aarch64)")
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
if(CMAKE_SYSTEM_VERSION MATCHES "10\\.0.*")
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_DEFINITIONS "_WIN32_WINNT=0x0A00")
|
|
||||||
endif()
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS /FI winapifamily.h)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Enable PIC for all targets except Windows and 32-bit x86.
|
|
||||||
# Avoid on 32-bit x86 due to register pressures.
|
|
||||||
if ((NOT CRYPTOPP_CROSS_COMPILE) AND (NOT (WINDOWS OR WINDOWS_STORE OR WINDOWS_PHONE)))
|
|
||||||
# Use Regex; match i386, i486, i586 and i686
|
|
||||||
if (NOT (${UNAME_MACHINE} MATCHES "i.86"))
|
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Link is driven through the compiler, but CXXFLAGS are not used. Also see
|
|
||||||
# http://public.kitware.com/pipermail/cmake/2003-June/003967.html
|
|
||||||
if (NOT (WINDOWS OR WINDOWS_STORE OR WINDOWS_PHONE))
|
|
||||||
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Sources & headers
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
# Library headers
|
|
||||||
file(GLOB cryptopp_HEADERS ${SRC_DIR}/*.h)
|
|
||||||
|
|
||||||
# Remove headers used to build test suite
|
|
||||||
list(REMOVE_ITEM cryptopp_HEADERS
|
|
||||||
${SRC_DIR}/bench.h
|
|
||||||
${SRC_DIR}/validate.h
|
|
||||||
)
|
|
||||||
|
|
||||||
# Library sources.
|
|
||||||
# These have been trimmed to include only things Citra uses. This speeds up
|
|
||||||
# compiles and reduces the amount of compilation breakage.
|
|
||||||
set(cryptopp_SOURCES
|
|
||||||
# The Crypto++ readme says you should put these 3 object files first,
|
|
||||||
# to avoid "problems associated with C++ static initialization order",
|
|
||||||
# but doesn't actually tell what could go wrong. Better safe than sorry
|
|
||||||
# I guess...
|
|
||||||
${SRC_DIR}/cryptlib.cpp
|
|
||||||
${SRC_DIR}/cpu.cpp
|
|
||||||
${SRC_DIR}/integer.cpp
|
|
||||||
|
|
||||||
${SRC_DIR}/algparam.cpp
|
|
||||||
${SRC_DIR}/allocate.cpp
|
|
||||||
${SRC_DIR}/asn.cpp
|
|
||||||
${SRC_DIR}/authenc.cpp
|
|
||||||
${SRC_DIR}/base64.cpp
|
|
||||||
${SRC_DIR}/basecode.cpp
|
|
||||||
${SRC_DIR}/ccm.cpp
|
|
||||||
${SRC_DIR}/crc_simd.cpp
|
|
||||||
${SRC_DIR}/des.cpp
|
|
||||||
${SRC_DIR}/dessp.cpp
|
|
||||||
${SRC_DIR}/dll.cpp
|
|
||||||
${SRC_DIR}/ec2n.cpp
|
|
||||||
${SRC_DIR}/ecp.cpp
|
|
||||||
${SRC_DIR}/filters.cpp
|
|
||||||
${SRC_DIR}/fips140.cpp
|
|
||||||
${SRC_DIR}/gcm_simd.cpp
|
|
||||||
${SRC_DIR}/gf2n_simd.cpp
|
|
||||||
${SRC_DIR}/gf2n.cpp
|
|
||||||
${SRC_DIR}/gfpcrypt.cpp
|
|
||||||
${SRC_DIR}/hex.cpp
|
|
||||||
${SRC_DIR}/hmac.cpp
|
|
||||||
${SRC_DIR}/hrtimer.cpp
|
|
||||||
${SRC_DIR}/iterhash.cpp
|
|
||||||
${SRC_DIR}/md5.cpp
|
|
||||||
${SRC_DIR}/misc.cpp
|
|
||||||
${SRC_DIR}/modes.cpp
|
|
||||||
${SRC_DIR}/mqueue.cpp
|
|
||||||
${SRC_DIR}/nbtheory.cpp
|
|
||||||
${SRC_DIR}/neon_simd.cpp
|
|
||||||
${SRC_DIR}/oaep.cpp
|
|
||||||
${SRC_DIR}/osrng.cpp
|
|
||||||
${SRC_DIR}/ppc_power7.cpp
|
|
||||||
${SRC_DIR}/ppc_power8.cpp
|
|
||||||
${SRC_DIR}/ppc_power9.cpp
|
|
||||||
${SRC_DIR}/ppc_simd.cpp
|
|
||||||
${SRC_DIR}/pubkey.cpp
|
|
||||||
${SRC_DIR}/queue.cpp
|
|
||||||
${SRC_DIR}/randpool.cpp
|
|
||||||
${SRC_DIR}/rdtables.cpp
|
|
||||||
${SRC_DIR}/rijndael_simd.cpp
|
|
||||||
${SRC_DIR}/rijndael.cpp
|
|
||||||
${SRC_DIR}/rng.cpp
|
|
||||||
${SRC_DIR}/sha_simd.cpp
|
|
||||||
${SRC_DIR}/sha.cpp
|
|
||||||
${SRC_DIR}/sse_simd.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
if(ANDROID)
|
|
||||||
include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
|
|
||||||
list(APPEND cryptopp_SOURCES ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(cryptopp_SOURCES_ASM)
|
|
||||||
|
|
||||||
if (MSVC AND NOT DISABLE_ASM)
|
|
||||||
if (${CMAKE_GENERATOR} MATCHES ".*ARM")
|
|
||||||
message(STATUS "Disabling ASM because ARM is specified as target platform.")
|
|
||||||
else ()
|
|
||||||
enable_language(ASM_MASM)
|
|
||||||
list(APPEND cryptopp_SOURCES_ASM
|
|
||||||
${SRC_DIR}/rdrand.asm
|
|
||||||
${SRC_DIR}/rdseed.asm
|
|
||||||
)
|
|
||||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
list(APPEND cryptopp_SOURCES_ASM
|
|
||||||
${SRC_DIR}/x64dll.asm
|
|
||||||
${SRC_DIR}/x64masm.asm
|
|
||||||
)
|
|
||||||
set_source_files_properties(${cryptopp_SOURCES_ASM} PROPERTIES COMPILE_DEFINITIONS "_M_X64")
|
|
||||||
else ()
|
|
||||||
set_source_files_properties(${cryptopp_SOURCES_ASM} PROPERTIES COMPILE_DEFINITIONS "_M_X86" COMPILE_FLAGS "/safeseh")
|
|
||||||
endif ()
|
|
||||||
set_source_files_properties(${cryptopp_SOURCES_ASM} PROPERTIES LANGUAGE ASM_MASM)
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Architecture flags
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER MATCHES "xlC")
|
|
||||||
|
|
||||||
if (CRYPTOPP_AMD64 OR CRYPTOPP_I386)
|
|
||||||
CheckCompileLinkOption("-msse2" CRYPTOPP_IA32_SSE2
|
|
||||||
"${TEST_PROG_DIR}/test_x86_sse2.cxx")
|
|
||||||
CheckCompileLinkOption("-mssse3" CRYPTOPP_IA32_SSSE3
|
|
||||||
"${TEST_PROG_DIR}/test_x86_ssse3.cxx")
|
|
||||||
CheckCompileLinkOption("-msse4.1" CRYPTOPP_IA32_SSE41
|
|
||||||
"${TEST_PROG_DIR}/test_x86_sse41.cxx")
|
|
||||||
CheckCompileLinkOption("-msse4.2" CRYPTOPP_IA32_SSE42
|
|
||||||
"${TEST_PROG_DIR}/test_x86_sse42.cxx")
|
|
||||||
CheckCompileLinkOption("-mssse3 -mpclmul" CRYPTOPP_IA32_CLMUL
|
|
||||||
"${TEST_PROG_DIR}/test_x86_clmul.cxx")
|
|
||||||
CheckCompileLinkOption("-msse4.1 -maes" CRYPTOPP_IA32_AES
|
|
||||||
"${TEST_PROG_DIR}/test_x86_aes.cxx")
|
|
||||||
CheckCompileLinkOption("-mavx" CRYPTOPP_IA32_AVX
|
|
||||||
"${TEST_PROG_DIR}/test_x86_avx.cxx")
|
|
||||||
CheckCompileLinkOption("-mavx2" CRYPTOPP_IA32_AVX2
|
|
||||||
"${TEST_PROG_DIR}/test_x86_avx2.cxx")
|
|
||||||
CheckCompileLinkOption("-msse4.2 -msha" CRYPTOPP_IA32_SHA
|
|
||||||
"${TEST_PROG_DIR}/test_x86_sha.cxx")
|
|
||||||
if (EXISTS "${TEST_PROG_DIR}/test_asm_mixed.cxx")
|
|
||||||
CheckCompileLinkOption("" CRYPTOPP_MIXED_ASM
|
|
||||||
"${TEST_PROG_DIR}/test_asm_mixed.cxx")
|
|
||||||
else ()
|
|
||||||
CheckCompileLinkOption("" CRYPTOPP_MIXED_ASM
|
|
||||||
"${TEST_PROG_DIR}/test_mixed_asm.cxx")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (NOT CRYPTOPP_MIXED_ASM)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_MIXED_ASM")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (NOT CRYPTOPP_IA32_SSE2 AND NOT CRYPTOPP_DISABLE_ASM)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_ASM")
|
|
||||||
elseif (CRYPTOPP_IA32_SSE2 AND NOT CRYPTOPP_DISABLE_ASM)
|
|
||||||
set_source_files_properties(${SRC_DIR}/sse_simd.cpp PROPERTIES COMPILE_FLAGS "-msse2")
|
|
||||||
endif ()
|
|
||||||
if (NOT CRYPTOPP_IA32_SSSE3 AND NOT CRYPTOPP_DISABLE_SSSE3)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_SSSE3")
|
|
||||||
elseif (CRYPTOPP_IA32_SSSE3 AND NOT CRYPTOPP_DISABLE_SSSE3)
|
|
||||||
if (NOT CRYPTOPP_IA32_SSE41 AND NOT CRYPTOPP_DISABLE_SSE4)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_SSE4")
|
|
||||||
endif ()
|
|
||||||
if (NOT CRYPTOPP_IA32_SSE42 AND NOT CRYPTOPP_DISABLE_SSE4)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_SSE4")
|
|
||||||
elseif (CRYPTOPP_IA32_SSE42 AND NOT CRYPTOPP_DISABLE_SSE4)
|
|
||||||
set_source_files_properties(${SRC_DIR}/crc_simd.cpp PROPERTIES COMPILE_FLAGS "-msse4.2")
|
|
||||||
if (NOT CRYPTOPP_IA32_CLMUL AND NOT CRYPTOPP_DISABLE_CLMUL)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_CLMUL")
|
|
||||||
elseif (CRYPTOPP_IA32_CLMUL AND NOT CRYPTOPP_DISABLE_CLMUL)
|
|
||||||
set_source_files_properties(${SRC_DIR}/gcm_simd.cpp PROPERTIES COMPILE_FLAGS "-mssse3 -mpclmul")
|
|
||||||
set_source_files_properties(${SRC_DIR}/gf2n_simd.cpp PROPERTIES COMPILE_FLAGS "-mpclmul")
|
|
||||||
endif ()
|
|
||||||
if (NOT CRYPTOPP_IA32_AES AND NOT CRYPTOPP_DISABLE_AES)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_AESNI")
|
|
||||||
elseif (CRYPTOPP_IA32_AES AND NOT CRYPTOPP_DISABLE_AES)
|
|
||||||
set_source_files_properties(${SRC_DIR}/rijndael_simd.cpp PROPERTIES COMPILE_FLAGS "-msse4.1 -maes")
|
|
||||||
endif ()
|
|
||||||
if (NOT CRYPTOPP_IA32_AVX2 AND NOT CRYPTOPP_DISABLE_AVX2)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_AVX2")
|
|
||||||
endif ()
|
|
||||||
if (NOT CRYPTOPP_IA32_SHA AND NOT CRYPTOPP_DISABLE_SHA)
|
|
||||||
list(APPEND CRYPTOPP_COMPILE_OPTIONS "-DCRYPTOPP_DISABLE_SHANI")
|
|
||||||
elseif (CRYPTOPP_IA32_SHA AND NOT CRYPTOPP_DISABLE_SHA)
|
|
||||||
set_source_files_properties(${SRC_DIR}/sha_simd.cpp PROPERTIES COMPILE_FLAGS "-msse4.2 -msha")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Compile targets
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
set(cryptopp_LIBRARY_SOURCES ${cryptopp_SOURCES_ASM})
|
|
||||||
list(APPEND cryptopp_LIBRARY_SOURCES ${cryptopp_SOURCES})
|
|
||||||
|
|
||||||
add_library(cryptopp STATIC ${cryptopp_LIBRARY_SOURCES})
|
|
||||||
target_compile_definitions(cryptopp PUBLIC ${CRYPTOPP_COMPILE_DEFINITIONS})
|
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
|
|
||||||
target_include_directories(cryptopp INTERFACE .)
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Third-party libraries
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
find_package(Threads)
|
|
||||||
target_link_libraries(cryptopp PRIVATE ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
|
|
||||||
if(ANDROID)
|
|
||||||
include(AndroidNdkModules)
|
|
||||||
android_ndk_import_module_cpufeatures()
|
|
||||||
target_link_libraries(cryptopp PRIVATE cpufeatures)
|
|
||||||
endif()
|
|
1
externals/cryptopp/cryptopp
vendored
1
externals/cryptopp/cryptopp
vendored
Submodule externals/cryptopp/cryptopp deleted from f2102243e6
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 7a926d689b...a179e2f2bb
305
externals/glad/include/glad/glad.h
vendored
305
externals/glad/include/glad/glad.h
vendored
@ -1,25 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Fri Sep 9 09:22:43 2022.
|
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Tue Feb 21 16:13:52 2023.
|
||||||
|
|
||||||
Language/Generator: C/C++
|
Language/Generator: C/C++
|
||||||
Specification: gl
|
Specification: gl
|
||||||
APIs: gl=4.4, gles2=3.2
|
APIs: gl=4.6, gles2=3.2
|
||||||
Profile: core
|
Profile: core
|
||||||
Extensions:
|
Extensions:
|
||||||
GL_ARB_buffer_storage,
|
GL_ARB_buffer_storage,
|
||||||
GL_ARB_direct_state_access,
|
GL_ARB_direct_state_access,
|
||||||
|
GL_ARB_texture_compression_bptc,
|
||||||
GL_EXT_buffer_storage,
|
GL_EXT_buffer_storage,
|
||||||
GL_EXT_clip_cull_distance
|
GL_EXT_clip_cull_distance,
|
||||||
|
GL_EXT_texture_compression_s3tc
|
||||||
Loader: True
|
Loader: True
|
||||||
Local files: False
|
Local files: False
|
||||||
Omit khrplatform: False
|
Omit khrplatform: False
|
||||||
Reproducible: False
|
Reproducible: False
|
||||||
|
|
||||||
Commandline:
|
Commandline:
|
||||||
--profile="core" --api="gl=4.4,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_direct_state_access,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance"
|
--profile="core" --api="gl=4.6,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_direct_state_access,GL_ARB_texture_compression_bptc,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_texture_compression_s3tc"
|
||||||
Online:
|
Online:
|
||||||
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.4&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_direct_state_access&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance
|
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.6&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_direct_state_access&extensions=GL_ARB_texture_compression_bptc&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_texture_compression_s3tc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -1472,6 +1474,62 @@ typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
|||||||
#define GL_QUERY_BUFFER_BINDING 0x9193
|
#define GL_QUERY_BUFFER_BINDING 0x9193
|
||||||
#define GL_QUERY_RESULT_NO_WAIT 0x9194
|
#define GL_QUERY_RESULT_NO_WAIT 0x9194
|
||||||
#define GL_MIRROR_CLAMP_TO_EDGE 0x8743
|
#define GL_MIRROR_CLAMP_TO_EDGE 0x8743
|
||||||
|
#define GL_CONTEXT_LOST 0x0507
|
||||||
|
#define GL_NEGATIVE_ONE_TO_ONE 0x935E
|
||||||
|
#define GL_ZERO_TO_ONE 0x935F
|
||||||
|
#define GL_CLIP_ORIGIN 0x935C
|
||||||
|
#define GL_CLIP_DEPTH_MODE 0x935D
|
||||||
|
#define GL_QUERY_WAIT_INVERTED 0x8E17
|
||||||
|
#define GL_QUERY_NO_WAIT_INVERTED 0x8E18
|
||||||
|
#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19
|
||||||
|
#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A
|
||||||
|
#define GL_MAX_CULL_DISTANCES 0x82F9
|
||||||
|
#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA
|
||||||
|
#define GL_TEXTURE_TARGET 0x1006
|
||||||
|
#define GL_QUERY_TARGET 0x82EA
|
||||||
|
#define GL_GUILTY_CONTEXT_RESET 0x8253
|
||||||
|
#define GL_INNOCENT_CONTEXT_RESET 0x8254
|
||||||
|
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
|
||||||
|
#define GL_RESET_NOTIFICATION_STRATEGY 0x8256
|
||||||
|
#define GL_LOSE_CONTEXT_ON_RESET 0x8252
|
||||||
|
#define GL_NO_RESET_NOTIFICATION 0x8261
|
||||||
|
#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004
|
||||||
|
#define GL_COLOR_TABLE 0x80D0
|
||||||
|
#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1
|
||||||
|
#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2
|
||||||
|
#define GL_PROXY_COLOR_TABLE 0x80D3
|
||||||
|
#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4
|
||||||
|
#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5
|
||||||
|
#define GL_CONVOLUTION_1D 0x8010
|
||||||
|
#define GL_CONVOLUTION_2D 0x8011
|
||||||
|
#define GL_SEPARABLE_2D 0x8012
|
||||||
|
#define GL_HISTOGRAM 0x8024
|
||||||
|
#define GL_PROXY_HISTOGRAM 0x8025
|
||||||
|
#define GL_MINMAX 0x802E
|
||||||
|
#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB
|
||||||
|
#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC
|
||||||
|
#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551
|
||||||
|
#define GL_SPIR_V_BINARY 0x9552
|
||||||
|
#define GL_PARAMETER_BUFFER 0x80EE
|
||||||
|
#define GL_PARAMETER_BUFFER_BINDING 0x80EF
|
||||||
|
#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008
|
||||||
|
#define GL_VERTICES_SUBMITTED 0x82EE
|
||||||
|
#define GL_PRIMITIVES_SUBMITTED 0x82EF
|
||||||
|
#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0
|
||||||
|
#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1
|
||||||
|
#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2
|
||||||
|
#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3
|
||||||
|
#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4
|
||||||
|
#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5
|
||||||
|
#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6
|
||||||
|
#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7
|
||||||
|
#define GL_POLYGON_OFFSET_CLAMP 0x8E1B
|
||||||
|
#define GL_SPIR_V_EXTENSIONS 0x9553
|
||||||
|
#define GL_NUM_SPIR_V_EXTENSIONS 0x9554
|
||||||
|
#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE
|
||||||
|
#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
|
||||||
|
#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC
|
||||||
|
#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED
|
||||||
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
|
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
|
||||||
#define GL_RED_BITS 0x0D52
|
#define GL_RED_BITS 0x0D52
|
||||||
#define GL_GREEN_BITS 0x0D53
|
#define GL_GREEN_BITS 0x0D53
|
||||||
@ -1501,14 +1559,6 @@ typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
|||||||
#define GL_HSL_COLOR 0x92AF
|
#define GL_HSL_COLOR 0x92AF
|
||||||
#define GL_HSL_LUMINOSITY 0x92B0
|
#define GL_HSL_LUMINOSITY 0x92B0
|
||||||
#define GL_PRIMITIVE_BOUNDING_BOX 0x92BE
|
#define GL_PRIMITIVE_BOUNDING_BOX 0x92BE
|
||||||
#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004
|
|
||||||
#define GL_LOSE_CONTEXT_ON_RESET 0x8252
|
|
||||||
#define GL_GUILTY_CONTEXT_RESET 0x8253
|
|
||||||
#define GL_INNOCENT_CONTEXT_RESET 0x8254
|
|
||||||
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
|
|
||||||
#define GL_RESET_NOTIFICATION_STRATEGY 0x8256
|
|
||||||
#define GL_NO_RESET_NOTIFICATION 0x8261
|
|
||||||
#define GL_CONTEXT_LOST 0x0507
|
|
||||||
#define GL_COMPRESSED_RGBA_ASTC_4x4 0x93B0
|
#define GL_COMPRESSED_RGBA_ASTC_4x4 0x93B0
|
||||||
#define GL_COMPRESSED_RGBA_ASTC_5x4 0x93B1
|
#define GL_COMPRESSED_RGBA_ASTC_5x4 0x93B1
|
||||||
#define GL_COMPRESSED_RGBA_ASTC_5x5 0x93B2
|
#define GL_COMPRESSED_RGBA_ASTC_5x5 0x93B2
|
||||||
@ -3324,73 +3374,12 @@ typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count,
|
|||||||
GLAPI PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers;
|
GLAPI PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers;
|
||||||
#define glBindVertexBuffers glad_glBindVertexBuffers
|
#define glBindVertexBuffers glad_glBindVertexBuffers
|
||||||
#endif
|
#endif
|
||||||
#ifndef GL_ES_VERSION_2_0
|
#ifndef GL_VERSION_4_5
|
||||||
#define GL_ES_VERSION_2_0 1
|
#define GL_VERSION_4_5 1
|
||||||
GLAPI int GLAD_GL_ES_VERSION_2_0;
|
GLAPI int GLAD_GL_VERSION_4_5;
|
||||||
#endif
|
typedef void (APIENTRYP PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth);
|
||||||
#ifndef GL_ES_VERSION_3_0
|
GLAPI PFNGLCLIPCONTROLPROC glad_glClipControl;
|
||||||
#define GL_ES_VERSION_3_0 1
|
#define glClipControl glad_glClipControl
|
||||||
GLAPI int GLAD_GL_ES_VERSION_3_0;
|
|
||||||
#endif
|
|
||||||
#ifndef GL_ES_VERSION_3_1
|
|
||||||
#define GL_ES_VERSION_3_1 1
|
|
||||||
GLAPI int GLAD_GL_ES_VERSION_3_1;
|
|
||||||
typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers);
|
|
||||||
GLAPI PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion;
|
|
||||||
#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion
|
|
||||||
#endif
|
|
||||||
#ifndef GL_ES_VERSION_3_2
|
|
||||||
#define GL_ES_VERSION_3_2 1
|
|
||||||
GLAPI int GLAD_GL_ES_VERSION_3_2;
|
|
||||||
typedef void (APIENTRYP PFNGLBLENDBARRIERPROC)(void);
|
|
||||||
GLAPI PFNGLBLENDBARRIERPROC glad_glBlendBarrier;
|
|
||||||
#define glBlendBarrier glad_glBlendBarrier
|
|
||||||
typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
|
|
||||||
GLAPI PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox;
|
|
||||||
#define glPrimitiveBoundingBox glad_glPrimitiveBoundingBox
|
|
||||||
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC)(void);
|
|
||||||
GLAPI PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus;
|
|
||||||
#define glGetGraphicsResetStatus glad_glGetGraphicsResetStatus
|
|
||||||
typedef void (APIENTRYP PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);
|
|
||||||
GLAPI PFNGLREADNPIXELSPROC glad_glReadnPixels;
|
|
||||||
#define glReadnPixels glad_glReadnPixels
|
|
||||||
typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params);
|
|
||||||
GLAPI PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv;
|
|
||||||
#define glGetnUniformfv glad_glGetnUniformfv
|
|
||||||
typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params);
|
|
||||||
GLAPI PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv;
|
|
||||||
#define glGetnUniformiv glad_glGetnUniformiv
|
|
||||||
typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params);
|
|
||||||
GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
|
|
||||||
#define glGetnUniformuiv glad_glGetnUniformuiv
|
|
||||||
#endif
|
|
||||||
#define GL_TEXTURE_TARGET 0x1006
|
|
||||||
#define GL_QUERY_TARGET 0x82EA
|
|
||||||
#define GL_MAP_PERSISTENT_BIT_EXT 0x0040
|
|
||||||
#define GL_MAP_COHERENT_BIT_EXT 0x0080
|
|
||||||
#define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100
|
|
||||||
#define GL_CLIENT_STORAGE_BIT_EXT 0x0200
|
|
||||||
#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT 0x00004000
|
|
||||||
#define GL_BUFFER_IMMUTABLE_STORAGE_EXT 0x821F
|
|
||||||
#define GL_BUFFER_STORAGE_FLAGS_EXT 0x8220
|
|
||||||
#define GL_MAX_CLIP_DISTANCES_EXT 0x0D32
|
|
||||||
#define GL_MAX_CULL_DISTANCES_EXT 0x82F9
|
|
||||||
#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT 0x82FA
|
|
||||||
#define GL_CLIP_DISTANCE0_EXT 0x3000
|
|
||||||
#define GL_CLIP_DISTANCE1_EXT 0x3001
|
|
||||||
#define GL_CLIP_DISTANCE2_EXT 0x3002
|
|
||||||
#define GL_CLIP_DISTANCE3_EXT 0x3003
|
|
||||||
#define GL_CLIP_DISTANCE4_EXT 0x3004
|
|
||||||
#define GL_CLIP_DISTANCE5_EXT 0x3005
|
|
||||||
#define GL_CLIP_DISTANCE6_EXT 0x3006
|
|
||||||
#define GL_CLIP_DISTANCE7_EXT 0x3007
|
|
||||||
#ifndef GL_ARB_buffer_storage
|
|
||||||
#define GL_ARB_buffer_storage 1
|
|
||||||
GLAPI int GLAD_GL_ARB_buffer_storage;
|
|
||||||
#endif
|
|
||||||
#ifndef GL_ARB_direct_state_access
|
|
||||||
#define GL_ARB_direct_state_access 1
|
|
||||||
GLAPI int GLAD_GL_ARB_direct_state_access;
|
|
||||||
typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids);
|
typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids);
|
||||||
GLAPI PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks;
|
GLAPI PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks;
|
||||||
#define glCreateTransformFeedbacks glad_glCreateTransformFeedbacks
|
#define glCreateTransformFeedbacks glad_glCreateTransformFeedbacks
|
||||||
@ -3682,6 +3671,158 @@ GLAPI PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v;
|
|||||||
typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||||
GLAPI PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv;
|
GLAPI PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv;
|
||||||
#define glGetQueryBufferObjectuiv glad_glGetQueryBufferObjectuiv
|
#define glGetQueryBufferObjectuiv glad_glGetQueryBufferObjectuiv
|
||||||
|
typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers);
|
||||||
|
GLAPI PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion;
|
||||||
|
#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion
|
||||||
|
typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels);
|
||||||
|
GLAPI PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage;
|
||||||
|
#define glGetTextureSubImage glad_glGetTextureSubImage
|
||||||
|
typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels);
|
||||||
|
GLAPI PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage;
|
||||||
|
#define glGetCompressedTextureSubImage glad_glGetCompressedTextureSubImage
|
||||||
|
typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC)(void);
|
||||||
|
GLAPI PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus;
|
||||||
|
#define glGetGraphicsResetStatus glad_glGetGraphicsResetStatus
|
||||||
|
typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint lod, GLsizei bufSize, void *pixels);
|
||||||
|
GLAPI PFNGLGETNCOMPRESSEDTEXIMAGEPROC glad_glGetnCompressedTexImage;
|
||||||
|
#define glGetnCompressedTexImage glad_glGetnCompressedTexImage
|
||||||
|
typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);
|
||||||
|
GLAPI PFNGLGETNTEXIMAGEPROC glad_glGetnTexImage;
|
||||||
|
#define glGetnTexImage glad_glGetnTexImage
|
||||||
|
typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble *params);
|
||||||
|
GLAPI PFNGLGETNUNIFORMDVPROC glad_glGetnUniformdv;
|
||||||
|
#define glGetnUniformdv glad_glGetnUniformdv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params);
|
||||||
|
GLAPI PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv;
|
||||||
|
#define glGetnUniformfv glad_glGetnUniformfv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params);
|
||||||
|
GLAPI PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv;
|
||||||
|
#define glGetnUniformiv glad_glGetnUniformiv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params);
|
||||||
|
GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
|
||||||
|
#define glGetnUniformuiv glad_glGetnUniformuiv
|
||||||
|
typedef void (APIENTRYP PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);
|
||||||
|
GLAPI PFNGLREADNPIXELSPROC glad_glReadnPixels;
|
||||||
|
#define glReadnPixels glad_glReadnPixels
|
||||||
|
typedef void (APIENTRYP PFNGLGETNMAPDVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble *v);
|
||||||
|
GLAPI PFNGLGETNMAPDVPROC glad_glGetnMapdv;
|
||||||
|
#define glGetnMapdv glad_glGetnMapdv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNMAPFVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat *v);
|
||||||
|
GLAPI PFNGLGETNMAPFVPROC glad_glGetnMapfv;
|
||||||
|
#define glGetnMapfv glad_glGetnMapfv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNMAPIVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint *v);
|
||||||
|
GLAPI PFNGLGETNMAPIVPROC glad_glGetnMapiv;
|
||||||
|
#define glGetnMapiv glad_glGetnMapiv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC)(GLenum map, GLsizei bufSize, GLfloat *values);
|
||||||
|
GLAPI PFNGLGETNPIXELMAPFVPROC glad_glGetnPixelMapfv;
|
||||||
|
#define glGetnPixelMapfv glad_glGetnPixelMapfv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC)(GLenum map, GLsizei bufSize, GLuint *values);
|
||||||
|
GLAPI PFNGLGETNPIXELMAPUIVPROC glad_glGetnPixelMapuiv;
|
||||||
|
#define glGetnPixelMapuiv glad_glGetnPixelMapuiv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC)(GLenum map, GLsizei bufSize, GLushort *values);
|
||||||
|
GLAPI PFNGLGETNPIXELMAPUSVPROC glad_glGetnPixelMapusv;
|
||||||
|
#define glGetnPixelMapusv glad_glGetnPixelMapusv
|
||||||
|
typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC)(GLsizei bufSize, GLubyte *pattern);
|
||||||
|
GLAPI PFNGLGETNPOLYGONSTIPPLEPROC glad_glGetnPolygonStipple;
|
||||||
|
#define glGetnPolygonStipple glad_glGetnPolygonStipple
|
||||||
|
typedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table);
|
||||||
|
GLAPI PFNGLGETNCOLORTABLEPROC glad_glGetnColorTable;
|
||||||
|
#define glGetnColorTable glad_glGetnColorTable
|
||||||
|
typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image);
|
||||||
|
GLAPI PFNGLGETNCONVOLUTIONFILTERPROC glad_glGetnConvolutionFilter;
|
||||||
|
#define glGetnConvolutionFilter glad_glGetnConvolutionFilter
|
||||||
|
typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span);
|
||||||
|
GLAPI PFNGLGETNSEPARABLEFILTERPROC glad_glGetnSeparableFilter;
|
||||||
|
#define glGetnSeparableFilter glad_glGetnSeparableFilter
|
||||||
|
typedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values);
|
||||||
|
GLAPI PFNGLGETNHISTOGRAMPROC glad_glGetnHistogram;
|
||||||
|
#define glGetnHistogram glad_glGetnHistogram
|
||||||
|
typedef void (APIENTRYP PFNGLGETNMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values);
|
||||||
|
GLAPI PFNGLGETNMINMAXPROC glad_glGetnMinmax;
|
||||||
|
#define glGetnMinmax glad_glGetnMinmax
|
||||||
|
typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC)(void);
|
||||||
|
GLAPI PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier;
|
||||||
|
#define glTextureBarrier glad_glTextureBarrier
|
||||||
|
#endif
|
||||||
|
#ifndef GL_VERSION_4_6
|
||||||
|
#define GL_VERSION_4_6 1
|
||||||
|
GLAPI int GLAD_GL_VERSION_4_6;
|
||||||
|
typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);
|
||||||
|
GLAPI PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader;
|
||||||
|
#define glSpecializeShader glad_glSpecializeShader
|
||||||
|
typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)(GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
|
||||||
|
GLAPI PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount;
|
||||||
|
#define glMultiDrawArraysIndirectCount glad_glMultiDrawArraysIndirectCount
|
||||||
|
typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)(GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
|
||||||
|
GLAPI PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount;
|
||||||
|
#define glMultiDrawElementsIndirectCount glad_glMultiDrawElementsIndirectCount
|
||||||
|
typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC)(GLfloat factor, GLfloat units, GLfloat clamp);
|
||||||
|
GLAPI PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp;
|
||||||
|
#define glPolygonOffsetClamp glad_glPolygonOffsetClamp
|
||||||
|
#endif
|
||||||
|
#ifndef GL_ES_VERSION_2_0
|
||||||
|
#define GL_ES_VERSION_2_0 1
|
||||||
|
GLAPI int GLAD_GL_ES_VERSION_2_0;
|
||||||
|
#endif
|
||||||
|
#ifndef GL_ES_VERSION_3_0
|
||||||
|
#define GL_ES_VERSION_3_0 1
|
||||||
|
GLAPI int GLAD_GL_ES_VERSION_3_0;
|
||||||
|
#endif
|
||||||
|
#ifndef GL_ES_VERSION_3_1
|
||||||
|
#define GL_ES_VERSION_3_1 1
|
||||||
|
GLAPI int GLAD_GL_ES_VERSION_3_1;
|
||||||
|
#endif
|
||||||
|
#ifndef GL_ES_VERSION_3_2
|
||||||
|
#define GL_ES_VERSION_3_2 1
|
||||||
|
GLAPI int GLAD_GL_ES_VERSION_3_2;
|
||||||
|
typedef void (APIENTRYP PFNGLBLENDBARRIERPROC)(void);
|
||||||
|
GLAPI PFNGLBLENDBARRIERPROC glad_glBlendBarrier;
|
||||||
|
#define glBlendBarrier glad_glBlendBarrier
|
||||||
|
typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
|
||||||
|
GLAPI PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox;
|
||||||
|
#define glPrimitiveBoundingBox glad_glPrimitiveBoundingBox
|
||||||
|
#endif
|
||||||
|
#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C
|
||||||
|
#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D
|
||||||
|
#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E
|
||||||
|
#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F
|
||||||
|
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
|
||||||
|
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
|
||||||
|
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
|
||||||
|
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
|
||||||
|
#define GL_MAP_PERSISTENT_BIT_EXT 0x0040
|
||||||
|
#define GL_MAP_COHERENT_BIT_EXT 0x0080
|
||||||
|
#define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100
|
||||||
|
#define GL_CLIENT_STORAGE_BIT_EXT 0x0200
|
||||||
|
#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT 0x00004000
|
||||||
|
#define GL_BUFFER_IMMUTABLE_STORAGE_EXT 0x821F
|
||||||
|
#define GL_BUFFER_STORAGE_FLAGS_EXT 0x8220
|
||||||
|
#define GL_MAX_CLIP_DISTANCES_EXT 0x0D32
|
||||||
|
#define GL_MAX_CULL_DISTANCES_EXT 0x82F9
|
||||||
|
#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT 0x82FA
|
||||||
|
#define GL_CLIP_DISTANCE0_EXT 0x3000
|
||||||
|
#define GL_CLIP_DISTANCE1_EXT 0x3001
|
||||||
|
#define GL_CLIP_DISTANCE2_EXT 0x3002
|
||||||
|
#define GL_CLIP_DISTANCE3_EXT 0x3003
|
||||||
|
#define GL_CLIP_DISTANCE4_EXT 0x3004
|
||||||
|
#define GL_CLIP_DISTANCE5_EXT 0x3005
|
||||||
|
#define GL_CLIP_DISTANCE6_EXT 0x3006
|
||||||
|
#define GL_CLIP_DISTANCE7_EXT 0x3007
|
||||||
|
#ifndef GL_ARB_buffer_storage
|
||||||
|
#define GL_ARB_buffer_storage 1
|
||||||
|
GLAPI int GLAD_GL_ARB_buffer_storage;
|
||||||
|
#endif
|
||||||
|
#ifndef GL_ARB_direct_state_access
|
||||||
|
#define GL_ARB_direct_state_access 1
|
||||||
|
GLAPI int GLAD_GL_ARB_direct_state_access;
|
||||||
|
#endif
|
||||||
|
#ifndef GL_ARB_texture_compression_bptc
|
||||||
|
#define GL_ARB_texture_compression_bptc 1
|
||||||
|
GLAPI int GLAD_GL_ARB_texture_compression_bptc;
|
||||||
|
#endif
|
||||||
|
#ifndef GL_EXT_texture_compression_s3tc
|
||||||
|
#define GL_EXT_texture_compression_s3tc 1
|
||||||
|
GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
|
||||||
#endif
|
#endif
|
||||||
#ifndef GL_EXT_buffer_storage
|
#ifndef GL_EXT_buffer_storage
|
||||||
#define GL_EXT_buffer_storage 1
|
#define GL_EXT_buffer_storage 1
|
||||||
@ -3694,6 +3835,10 @@ GLAPI PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT;
|
|||||||
#define GL_EXT_clip_cull_distance 1
|
#define GL_EXT_clip_cull_distance 1
|
||||||
GLAPI int GLAD_GL_EXT_clip_cull_distance;
|
GLAPI int GLAD_GL_EXT_clip_cull_distance;
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef GL_EXT_texture_compression_s3tc
|
||||||
|
#define GL_EXT_texture_compression_s3tc 1
|
||||||
|
GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
376
externals/glad/src/glad.c
vendored
376
externals/glad/src/glad.c
vendored
@ -1,25 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Fri Sep 9 09:22:43 2022.
|
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Tue Feb 21 16:13:52 2023.
|
||||||
|
|
||||||
Language/Generator: C/C++
|
Language/Generator: C/C++
|
||||||
Specification: gl
|
Specification: gl
|
||||||
APIs: gl=4.4, gles2=3.2
|
APIs: gl=4.6, gles2=3.2
|
||||||
Profile: core
|
Profile: core
|
||||||
Extensions:
|
Extensions:
|
||||||
GL_ARB_buffer_storage,
|
GL_ARB_buffer_storage,
|
||||||
GL_ARB_direct_state_access,
|
GL_ARB_direct_state_access,
|
||||||
|
GL_ARB_texture_compression_bptc,
|
||||||
GL_EXT_buffer_storage,
|
GL_EXT_buffer_storage,
|
||||||
GL_EXT_clip_cull_distance
|
GL_EXT_clip_cull_distance,
|
||||||
|
GL_EXT_texture_compression_s3tc
|
||||||
Loader: True
|
Loader: True
|
||||||
Local files: False
|
Local files: False
|
||||||
Omit khrplatform: False
|
Omit khrplatform: False
|
||||||
Reproducible: False
|
Reproducible: False
|
||||||
|
|
||||||
Commandline:
|
Commandline:
|
||||||
--profile="core" --api="gl=4.4,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_direct_state_access,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance"
|
--profile="core" --api="gl=4.6,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_direct_state_access,GL_ARB_texture_compression_bptc,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_texture_compression_s3tc"
|
||||||
Online:
|
Online:
|
||||||
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.4&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_direct_state_access&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance
|
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.6&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_direct_state_access&extensions=GL_ARB_texture_compression_bptc&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_texture_compression_s3tc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -275,6 +277,8 @@ int GLAD_GL_VERSION_4_1 = 0;
|
|||||||
int GLAD_GL_VERSION_4_2 = 0;
|
int GLAD_GL_VERSION_4_2 = 0;
|
||||||
int GLAD_GL_VERSION_4_3 = 0;
|
int GLAD_GL_VERSION_4_3 = 0;
|
||||||
int GLAD_GL_VERSION_4_4 = 0;
|
int GLAD_GL_VERSION_4_4 = 0;
|
||||||
|
int GLAD_GL_VERSION_4_5 = 0;
|
||||||
|
int GLAD_GL_VERSION_4_6 = 0;
|
||||||
int GLAD_GL_ES_VERSION_2_0 = 0;
|
int GLAD_GL_ES_VERSION_2_0 = 0;
|
||||||
int GLAD_GL_ES_VERSION_3_0 = 0;
|
int GLAD_GL_ES_VERSION_3_0 = 0;
|
||||||
int GLAD_GL_ES_VERSION_3_1 = 0;
|
int GLAD_GL_ES_VERSION_3_1 = 0;
|
||||||
@ -302,6 +306,7 @@ PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;
|
|||||||
PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;
|
PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;
|
||||||
PFNGLBINDSAMPLERSPROC glad_glBindSamplers = NULL;
|
PFNGLBINDSAMPLERSPROC glad_glBindSamplers = NULL;
|
||||||
PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
|
PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
|
||||||
|
PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit = NULL;
|
||||||
PFNGLBINDTEXTURESPROC glad_glBindTextures = NULL;
|
PFNGLBINDTEXTURESPROC glad_glBindTextures = NULL;
|
||||||
PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL;
|
PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL;
|
||||||
PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;
|
PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;
|
||||||
@ -318,10 +323,12 @@ PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;
|
|||||||
PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL;
|
PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL;
|
||||||
PFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL;
|
PFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL;
|
||||||
PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;
|
PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;
|
||||||
|
PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL;
|
||||||
PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
|
PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
|
||||||
PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;
|
PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;
|
||||||
PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;
|
PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;
|
||||||
PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;
|
PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;
|
||||||
|
PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus = NULL;
|
||||||
PFNGLCLAMPCOLORPROC glad_glClampColor = NULL;
|
PFNGLCLAMPCOLORPROC glad_glClampColor = NULL;
|
||||||
PFNGLCLEARPROC glad_glClear = NULL;
|
PFNGLCLEARPROC glad_glClear = NULL;
|
||||||
PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData = NULL;
|
PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData = NULL;
|
||||||
@ -333,10 +340,17 @@ PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL;
|
|||||||
PFNGLCLEARCOLORPROC glad_glClearColor = NULL;
|
PFNGLCLEARCOLORPROC glad_glClearColor = NULL;
|
||||||
PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;
|
PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;
|
||||||
PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL;
|
PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL;
|
||||||
|
PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData = NULL;
|
||||||
|
PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData = NULL;
|
||||||
|
PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi = NULL;
|
||||||
|
PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv = NULL;
|
||||||
|
PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv = NULL;
|
||||||
|
PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv = NULL;
|
||||||
PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
|
PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
|
||||||
PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL;
|
PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL;
|
||||||
PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL;
|
PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL;
|
||||||
PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;
|
PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;
|
||||||
|
PFNGLCLIPCONTROLPROC glad_glClipControl = NULL;
|
||||||
PFNGLCOLORMASKPROC glad_glColorMask = NULL;
|
PFNGLCOLORMASKPROC glad_glColorMask = NULL;
|
||||||
PFNGLCOLORMASKIPROC glad_glColorMaski = NULL;
|
PFNGLCOLORMASKIPROC glad_glColorMaski = NULL;
|
||||||
PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;
|
PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;
|
||||||
@ -350,16 +364,32 @@ PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL;
|
|||||||
PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;
|
PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;
|
||||||
PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;
|
PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;
|
||||||
PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;
|
PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;
|
||||||
|
PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D = NULL;
|
||||||
|
PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D = NULL;
|
||||||
|
PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D = NULL;
|
||||||
PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;
|
PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;
|
||||||
PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL;
|
PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL;
|
||||||
|
PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData = NULL;
|
||||||
PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;
|
PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;
|
||||||
PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;
|
PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;
|
||||||
PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;
|
PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;
|
||||||
PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;
|
PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;
|
||||||
PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;
|
PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;
|
||||||
|
PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D = NULL;
|
||||||
|
PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D = NULL;
|
||||||
|
PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D = NULL;
|
||||||
|
PFNGLCREATEBUFFERSPROC glad_glCreateBuffers = NULL;
|
||||||
|
PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers = NULL;
|
||||||
PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;
|
PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;
|
||||||
|
PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines = NULL;
|
||||||
|
PFNGLCREATEQUERIESPROC glad_glCreateQueries = NULL;
|
||||||
|
PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers = NULL;
|
||||||
|
PFNGLCREATESAMPLERSPROC glad_glCreateSamplers = NULL;
|
||||||
PFNGLCREATESHADERPROC glad_glCreateShader = NULL;
|
PFNGLCREATESHADERPROC glad_glCreateShader = NULL;
|
||||||
PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL;
|
PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL;
|
||||||
|
PFNGLCREATETEXTURESPROC glad_glCreateTextures = NULL;
|
||||||
|
PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks = NULL;
|
||||||
|
PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays = NULL;
|
||||||
PFNGLCULLFACEPROC glad_glCullFace = NULL;
|
PFNGLCULLFACEPROC glad_glCullFace = NULL;
|
||||||
PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL;
|
PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL;
|
||||||
PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL;
|
PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL;
|
||||||
@ -384,6 +414,7 @@ PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL;
|
|||||||
PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
|
PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
|
||||||
PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
|
PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
|
||||||
PFNGLDISABLEPROC glad_glDisable = NULL;
|
PFNGLDISABLEPROC glad_glDisable = NULL;
|
||||||
|
PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib = NULL;
|
||||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;
|
PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;
|
||||||
PFNGLDISABLEIPROC glad_glDisablei = NULL;
|
PFNGLDISABLEIPROC glad_glDisablei = NULL;
|
||||||
PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL;
|
PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL;
|
||||||
@ -408,6 +439,7 @@ PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced =
|
|||||||
PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream = NULL;
|
PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream = NULL;
|
||||||
PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced = NULL;
|
PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced = NULL;
|
||||||
PFNGLENABLEPROC glad_glEnable = NULL;
|
PFNGLENABLEPROC glad_glEnable = NULL;
|
||||||
|
PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib = NULL;
|
||||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;
|
PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;
|
||||||
PFNGLENABLEIPROC glad_glEnablei = NULL;
|
PFNGLENABLEIPROC glad_glEnablei = NULL;
|
||||||
PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;
|
PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;
|
||||||
@ -418,6 +450,7 @@ PFNGLFENCESYNCPROC glad_glFenceSync = NULL;
|
|||||||
PFNGLFINISHPROC glad_glFinish = NULL;
|
PFNGLFINISHPROC glad_glFinish = NULL;
|
||||||
PFNGLFLUSHPROC glad_glFlush = NULL;
|
PFNGLFLUSHPROC glad_glFlush = NULL;
|
||||||
PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;
|
PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;
|
||||||
|
PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange = NULL;
|
||||||
PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL;
|
PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL;
|
||||||
PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;
|
PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;
|
||||||
PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL;
|
PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL;
|
||||||
@ -436,6 +469,7 @@ PFNGLGENTEXTURESPROC glad_glGenTextures = NULL;
|
|||||||
PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL;
|
PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL;
|
||||||
PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;
|
PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;
|
||||||
PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
|
PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
|
||||||
|
PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap = NULL;
|
||||||
PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv = NULL;
|
PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv = NULL;
|
||||||
PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;
|
PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;
|
||||||
PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName = NULL;
|
PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName = NULL;
|
||||||
@ -455,6 +489,8 @@ PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;
|
|||||||
PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;
|
PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;
|
||||||
PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;
|
PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;
|
||||||
PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;
|
PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;
|
||||||
|
PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage = NULL;
|
||||||
|
PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage = NULL;
|
||||||
PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL;
|
PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL;
|
||||||
PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v = NULL;
|
PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v = NULL;
|
||||||
PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;
|
PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;
|
||||||
@ -473,6 +509,13 @@ PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;
|
|||||||
PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v = NULL;
|
PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v = NULL;
|
||||||
PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL;
|
PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL;
|
||||||
PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;
|
PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;
|
||||||
|
PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v = NULL;
|
||||||
|
PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv = NULL;
|
||||||
|
PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv = NULL;
|
||||||
|
PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData = NULL;
|
||||||
|
PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv = NULL;
|
||||||
|
PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv = NULL;
|
||||||
|
PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv = NULL;
|
||||||
PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL;
|
PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL;
|
||||||
PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL;
|
PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL;
|
||||||
PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;
|
PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;
|
||||||
@ -488,6 +531,10 @@ PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName = NULL;
|
|||||||
PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL;
|
PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL;
|
||||||
PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv = NULL;
|
PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv = NULL;
|
||||||
PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
|
PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
|
||||||
|
PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v = NULL;
|
||||||
|
PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv = NULL;
|
||||||
|
PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v = NULL;
|
||||||
|
PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv = NULL;
|
||||||
PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv = NULL;
|
PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv = NULL;
|
||||||
PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
|
PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
|
||||||
PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;
|
PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;
|
||||||
@ -515,7 +562,18 @@ PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL;
|
|||||||
PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;
|
PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;
|
||||||
PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;
|
PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;
|
||||||
PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;
|
PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;
|
||||||
|
PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage = NULL;
|
||||||
|
PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv = NULL;
|
||||||
|
PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv = NULL;
|
||||||
|
PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv = NULL;
|
||||||
|
PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv = NULL;
|
||||||
|
PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv = NULL;
|
||||||
|
PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv = NULL;
|
||||||
|
PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage = NULL;
|
||||||
PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;
|
PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;
|
||||||
|
PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v = NULL;
|
||||||
|
PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v = NULL;
|
||||||
|
PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv = NULL;
|
||||||
PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;
|
PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;
|
||||||
PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;
|
PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;
|
||||||
PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;
|
PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;
|
||||||
@ -524,6 +582,9 @@ PFNGLGETUNIFORMDVPROC glad_glGetUniformdv = NULL;
|
|||||||
PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;
|
PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;
|
||||||
PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;
|
PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;
|
||||||
PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;
|
PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;
|
||||||
|
PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv = NULL;
|
||||||
|
PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv = NULL;
|
||||||
|
PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv = NULL;
|
||||||
PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;
|
PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;
|
||||||
PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;
|
PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;
|
||||||
PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv = NULL;
|
PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv = NULL;
|
||||||
@ -531,6 +592,21 @@ PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;
|
|||||||
PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;
|
PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;
|
||||||
PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;
|
PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;
|
||||||
PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;
|
PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;
|
||||||
|
PFNGLGETNCOLORTABLEPROC glad_glGetnColorTable = NULL;
|
||||||
|
PFNGLGETNCOMPRESSEDTEXIMAGEPROC glad_glGetnCompressedTexImage = NULL;
|
||||||
|
PFNGLGETNCONVOLUTIONFILTERPROC glad_glGetnConvolutionFilter = NULL;
|
||||||
|
PFNGLGETNHISTOGRAMPROC glad_glGetnHistogram = NULL;
|
||||||
|
PFNGLGETNMAPDVPROC glad_glGetnMapdv = NULL;
|
||||||
|
PFNGLGETNMAPFVPROC glad_glGetnMapfv = NULL;
|
||||||
|
PFNGLGETNMAPIVPROC glad_glGetnMapiv = NULL;
|
||||||
|
PFNGLGETNMINMAXPROC glad_glGetnMinmax = NULL;
|
||||||
|
PFNGLGETNPIXELMAPFVPROC glad_glGetnPixelMapfv = NULL;
|
||||||
|
PFNGLGETNPIXELMAPUIVPROC glad_glGetnPixelMapuiv = NULL;
|
||||||
|
PFNGLGETNPIXELMAPUSVPROC glad_glGetnPixelMapusv = NULL;
|
||||||
|
PFNGLGETNPOLYGONSTIPPLEPROC glad_glGetnPolygonStipple = NULL;
|
||||||
|
PFNGLGETNSEPARABLEFILTERPROC glad_glGetnSeparableFilter = NULL;
|
||||||
|
PFNGLGETNTEXIMAGEPROC glad_glGetnTexImage = NULL;
|
||||||
|
PFNGLGETNUNIFORMDVPROC glad_glGetnUniformdv = NULL;
|
||||||
PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv = NULL;
|
PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv = NULL;
|
||||||
PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv = NULL;
|
PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv = NULL;
|
||||||
PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv = NULL;
|
PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv = NULL;
|
||||||
@ -538,6 +614,8 @@ PFNGLHINTPROC glad_glHint = NULL;
|
|||||||
PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData = NULL;
|
PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData = NULL;
|
||||||
PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData = NULL;
|
PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData = NULL;
|
||||||
PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL;
|
PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL;
|
||||||
|
PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData = NULL;
|
||||||
|
PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData = NULL;
|
||||||
PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL;
|
PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL;
|
||||||
PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage = NULL;
|
PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage = NULL;
|
||||||
PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage = NULL;
|
PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage = NULL;
|
||||||
@ -560,14 +638,18 @@ PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;
|
|||||||
PFNGLLOGICOPPROC glad_glLogicOp = NULL;
|
PFNGLLOGICOPPROC glad_glLogicOp = NULL;
|
||||||
PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;
|
PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;
|
||||||
PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;
|
PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;
|
||||||
|
PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer = NULL;
|
||||||
|
PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange = NULL;
|
||||||
PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL;
|
PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL;
|
||||||
PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL;
|
PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL;
|
||||||
PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading = NULL;
|
PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading = NULL;
|
||||||
PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;
|
PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;
|
||||||
PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect = NULL;
|
PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect = NULL;
|
||||||
|
PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount = NULL;
|
||||||
PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;
|
PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;
|
||||||
PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;
|
PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;
|
||||||
PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect = NULL;
|
PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect = NULL;
|
||||||
|
PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount = NULL;
|
||||||
PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;
|
PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;
|
||||||
PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;
|
PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;
|
||||||
PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;
|
PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;
|
||||||
@ -576,6 +658,18 @@ PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;
|
|||||||
PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
|
PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
|
||||||
PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
|
PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
|
||||||
PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
|
PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
|
||||||
|
PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData = NULL;
|
||||||
|
PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage = NULL;
|
||||||
|
PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture = NULL;
|
||||||
|
PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer = NULL;
|
||||||
|
PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage = NULL;
|
||||||
|
PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample = NULL;
|
||||||
PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;
|
PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;
|
||||||
PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;
|
PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;
|
||||||
PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL;
|
PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL;
|
||||||
@ -592,6 +686,7 @@ PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL;
|
|||||||
PFNGLPOINTSIZEPROC glad_glPointSize = NULL;
|
PFNGLPOINTSIZEPROC glad_glPointSize = NULL;
|
||||||
PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;
|
PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;
|
||||||
PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
|
PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
|
||||||
|
PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp = NULL;
|
||||||
PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL;
|
PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL;
|
||||||
PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox = NULL;
|
PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox = NULL;
|
||||||
PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
|
PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
|
||||||
@ -674,6 +769,7 @@ PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL;
|
|||||||
PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL;
|
PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL;
|
||||||
PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
|
PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
|
||||||
PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding = NULL;
|
PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding = NULL;
|
||||||
|
PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader = NULL;
|
||||||
PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
|
PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
|
||||||
PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
|
PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
|
||||||
PFNGLSTENCILMASKPROC glad_glStencilMask = NULL;
|
PFNGLSTENCILMASKPROC glad_glStencilMask = NULL;
|
||||||
@ -709,7 +805,26 @@ PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample = NULL;
|
|||||||
PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;
|
PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;
|
||||||
PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;
|
PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;
|
||||||
PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;
|
PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;
|
||||||
|
PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier = NULL;
|
||||||
|
PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer = NULL;
|
||||||
|
PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange = NULL;
|
||||||
|
PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv = NULL;
|
||||||
|
PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv = NULL;
|
||||||
|
PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf = NULL;
|
||||||
|
PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv = NULL;
|
||||||
|
PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri = NULL;
|
||||||
|
PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv = NULL;
|
||||||
|
PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D = NULL;
|
||||||
|
PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D = NULL;
|
||||||
|
PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample = NULL;
|
||||||
|
PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D = NULL;
|
||||||
|
PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample = NULL;
|
||||||
|
PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D = NULL;
|
||||||
|
PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D = NULL;
|
||||||
|
PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D = NULL;
|
||||||
PFNGLTEXTUREVIEWPROC glad_glTextureView = NULL;
|
PFNGLTEXTUREVIEWPROC glad_glTextureView = NULL;
|
||||||
|
PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase = NULL;
|
||||||
|
PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange = NULL;
|
||||||
PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;
|
PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;
|
||||||
PFNGLUNIFORM1DPROC glad_glUniform1d = NULL;
|
PFNGLUNIFORM1DPROC glad_glUniform1d = NULL;
|
||||||
PFNGLUNIFORM1DVPROC glad_glUniform1dv = NULL;
|
PFNGLUNIFORM1DVPROC glad_glUniform1dv = NULL;
|
||||||
@ -764,10 +879,19 @@ PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv = NULL;
|
|||||||
PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;
|
PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;
|
||||||
PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv = NULL;
|
PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv = NULL;
|
||||||
PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;
|
PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;
|
||||||
|
PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer = NULL;
|
||||||
PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;
|
PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;
|
||||||
PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL;
|
PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL;
|
||||||
PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;
|
PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;
|
||||||
PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL;
|
PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL;
|
||||||
|
PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding = NULL;
|
||||||
|
PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat = NULL;
|
||||||
|
PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat = NULL;
|
||||||
|
PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat = NULL;
|
||||||
|
PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor = NULL;
|
||||||
|
PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer = NULL;
|
||||||
|
PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer = NULL;
|
||||||
|
PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers = NULL;
|
||||||
PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;
|
PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;
|
||||||
PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;
|
PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;
|
||||||
PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;
|
PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;
|
||||||
@ -862,105 +986,10 @@ PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv = NULL;
|
|||||||
PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
|
PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
|
||||||
int GLAD_GL_ARB_buffer_storage = 0;
|
int GLAD_GL_ARB_buffer_storage = 0;
|
||||||
int GLAD_GL_ARB_direct_state_access = 0;
|
int GLAD_GL_ARB_direct_state_access = 0;
|
||||||
|
int GLAD_GL_ARB_texture_compression_bptc = 0;
|
||||||
int GLAD_GL_EXT_buffer_storage = 0;
|
int GLAD_GL_EXT_buffer_storage = 0;
|
||||||
int GLAD_GL_EXT_clip_cull_distance = 0;
|
int GLAD_GL_EXT_clip_cull_distance = 0;
|
||||||
PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks = NULL;
|
int GLAD_GL_EXT_texture_compression_s3tc = 0;
|
||||||
PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase = NULL;
|
|
||||||
PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange = NULL;
|
|
||||||
PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv = NULL;
|
|
||||||
PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v = NULL;
|
|
||||||
PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v = NULL;
|
|
||||||
PFNGLCREATEBUFFERSPROC glad_glCreateBuffers = NULL;
|
|
||||||
PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage = NULL;
|
|
||||||
PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData = NULL;
|
|
||||||
PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData = NULL;
|
|
||||||
PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData = NULL;
|
|
||||||
PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData = NULL;
|
|
||||||
PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData = NULL;
|
|
||||||
PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer = NULL;
|
|
||||||
PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange = NULL;
|
|
||||||
PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer = NULL;
|
|
||||||
PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange = NULL;
|
|
||||||
PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv = NULL;
|
|
||||||
PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v = NULL;
|
|
||||||
PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv = NULL;
|
|
||||||
PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData = NULL;
|
|
||||||
PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers = NULL;
|
|
||||||
PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer = NULL;
|
|
||||||
PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData = NULL;
|
|
||||||
PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData = NULL;
|
|
||||||
PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv = NULL;
|
|
||||||
PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv = NULL;
|
|
||||||
PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv = NULL;
|
|
||||||
PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi = NULL;
|
|
||||||
PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL;
|
|
||||||
PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus = NULL;
|
|
||||||
PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv = NULL;
|
|
||||||
PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv = NULL;
|
|
||||||
PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers = NULL;
|
|
||||||
PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage = NULL;
|
|
||||||
PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample = NULL;
|
|
||||||
PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv = NULL;
|
|
||||||
PFNGLCREATETEXTURESPROC glad_glCreateTextures = NULL;
|
|
||||||
PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer = NULL;
|
|
||||||
PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange = NULL;
|
|
||||||
PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D = NULL;
|
|
||||||
PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D = NULL;
|
|
||||||
PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D = NULL;
|
|
||||||
PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample = NULL;
|
|
||||||
PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample = NULL;
|
|
||||||
PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D = NULL;
|
|
||||||
PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D = NULL;
|
|
||||||
PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D = NULL;
|
|
||||||
PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D = NULL;
|
|
||||||
PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D = NULL;
|
|
||||||
PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D = NULL;
|
|
||||||
PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D = NULL;
|
|
||||||
PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D = NULL;
|
|
||||||
PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D = NULL;
|
|
||||||
PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf = NULL;
|
|
||||||
PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv = NULL;
|
|
||||||
PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri = NULL;
|
|
||||||
PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv = NULL;
|
|
||||||
PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv = NULL;
|
|
||||||
PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv = NULL;
|
|
||||||
PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap = NULL;
|
|
||||||
PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit = NULL;
|
|
||||||
PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage = NULL;
|
|
||||||
PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage = NULL;
|
|
||||||
PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv = NULL;
|
|
||||||
PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv = NULL;
|
|
||||||
PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv = NULL;
|
|
||||||
PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv = NULL;
|
|
||||||
PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv = NULL;
|
|
||||||
PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv = NULL;
|
|
||||||
PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays = NULL;
|
|
||||||
PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib = NULL;
|
|
||||||
PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib = NULL;
|
|
||||||
PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer = NULL;
|
|
||||||
PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer = NULL;
|
|
||||||
PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers = NULL;
|
|
||||||
PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding = NULL;
|
|
||||||
PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat = NULL;
|
|
||||||
PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat = NULL;
|
|
||||||
PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat = NULL;
|
|
||||||
PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor = NULL;
|
|
||||||
PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv = NULL;
|
|
||||||
PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv = NULL;
|
|
||||||
PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv = NULL;
|
|
||||||
PFNGLCREATESAMPLERSPROC glad_glCreateSamplers = NULL;
|
|
||||||
PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines = NULL;
|
|
||||||
PFNGLCREATEQUERIESPROC glad_glCreateQueries = NULL;
|
|
||||||
PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v = NULL;
|
|
||||||
PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv = NULL;
|
|
||||||
PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v = NULL;
|
|
||||||
PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv = NULL;
|
|
||||||
PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT = NULL;
|
PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT = NULL;
|
||||||
static void load_GL_VERSION_1_0(GLADloadproc load) {
|
static void load_GL_VERSION_1_0(GLADloadproc load) {
|
||||||
if(!GLAD_GL_VERSION_1_0) return;
|
if(!GLAD_GL_VERSION_1_0) return;
|
||||||
@ -1590,6 +1619,138 @@ static void load_GL_VERSION_4_4(GLADloadproc load) {
|
|||||||
glad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)load("glBindImageTextures");
|
glad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)load("glBindImageTextures");
|
||||||
glad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)load("glBindVertexBuffers");
|
glad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)load("glBindVertexBuffers");
|
||||||
}
|
}
|
||||||
|
static void load_GL_VERSION_4_5(GLADloadproc load) {
|
||||||
|
if(!GLAD_GL_VERSION_4_5) return;
|
||||||
|
glad_glClipControl = (PFNGLCLIPCONTROLPROC)load("glClipControl");
|
||||||
|
glad_glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)load("glCreateTransformFeedbacks");
|
||||||
|
glad_glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)load("glTransformFeedbackBufferBase");
|
||||||
|
glad_glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)load("glTransformFeedbackBufferRange");
|
||||||
|
glad_glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC)load("glGetTransformFeedbackiv");
|
||||||
|
glad_glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC)load("glGetTransformFeedbacki_v");
|
||||||
|
glad_glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC)load("glGetTransformFeedbacki64_v");
|
||||||
|
glad_glCreateBuffers = (PFNGLCREATEBUFFERSPROC)load("glCreateBuffers");
|
||||||
|
glad_glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC)load("glNamedBufferStorage");
|
||||||
|
glad_glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC)load("glNamedBufferData");
|
||||||
|
glad_glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)load("glNamedBufferSubData");
|
||||||
|
glad_glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC)load("glCopyNamedBufferSubData");
|
||||||
|
glad_glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC)load("glClearNamedBufferData");
|
||||||
|
glad_glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC)load("glClearNamedBufferSubData");
|
||||||
|
glad_glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC)load("glMapNamedBuffer");
|
||||||
|
glad_glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC)load("glMapNamedBufferRange");
|
||||||
|
glad_glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC)load("glUnmapNamedBuffer");
|
||||||
|
glad_glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)load("glFlushMappedNamedBufferRange");
|
||||||
|
glad_glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC)load("glGetNamedBufferParameteriv");
|
||||||
|
glad_glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)load("glGetNamedBufferParameteri64v");
|
||||||
|
glad_glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC)load("glGetNamedBufferPointerv");
|
||||||
|
glad_glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)load("glGetNamedBufferSubData");
|
||||||
|
glad_glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC)load("glCreateFramebuffers");
|
||||||
|
glad_glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)load("glNamedFramebufferRenderbuffer");
|
||||||
|
glad_glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)load("glNamedFramebufferParameteri");
|
||||||
|
glad_glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)load("glNamedFramebufferTexture");
|
||||||
|
glad_glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)load("glNamedFramebufferTextureLayer");
|
||||||
|
glad_glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)load("glNamedFramebufferDrawBuffer");
|
||||||
|
glad_glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)load("glNamedFramebufferDrawBuffers");
|
||||||
|
glad_glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)load("glNamedFramebufferReadBuffer");
|
||||||
|
glad_glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)load("glInvalidateNamedFramebufferData");
|
||||||
|
glad_glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)load("glInvalidateNamedFramebufferSubData");
|
||||||
|
glad_glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)load("glClearNamedFramebufferiv");
|
||||||
|
glad_glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)load("glClearNamedFramebufferuiv");
|
||||||
|
glad_glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)load("glClearNamedFramebufferfv");
|
||||||
|
glad_glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)load("glClearNamedFramebufferfi");
|
||||||
|
glad_glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)load("glBlitNamedFramebuffer");
|
||||||
|
glad_glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)load("glCheckNamedFramebufferStatus");
|
||||||
|
glad_glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)load("glGetNamedFramebufferParameteriv");
|
||||||
|
glad_glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetNamedFramebufferAttachmentParameteriv");
|
||||||
|
glad_glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC)load("glCreateRenderbuffers");
|
||||||
|
glad_glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC)load("glNamedRenderbufferStorage");
|
||||||
|
glad_glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glNamedRenderbufferStorageMultisample");
|
||||||
|
glad_glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)load("glGetNamedRenderbufferParameteriv");
|
||||||
|
glad_glCreateTextures = (PFNGLCREATETEXTURESPROC)load("glCreateTextures");
|
||||||
|
glad_glTextureBuffer = (PFNGLTEXTUREBUFFERPROC)load("glTextureBuffer");
|
||||||
|
glad_glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC)load("glTextureBufferRange");
|
||||||
|
glad_glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC)load("glTextureStorage1D");
|
||||||
|
glad_glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC)load("glTextureStorage2D");
|
||||||
|
glad_glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC)load("glTextureStorage3D");
|
||||||
|
glad_glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)load("glTextureStorage2DMultisample");
|
||||||
|
glad_glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)load("glTextureStorage3DMultisample");
|
||||||
|
glad_glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC)load("glTextureSubImage1D");
|
||||||
|
glad_glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC)load("glTextureSubImage2D");
|
||||||
|
glad_glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC)load("glTextureSubImage3D");
|
||||||
|
glad_glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)load("glCompressedTextureSubImage1D");
|
||||||
|
glad_glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)load("glCompressedTextureSubImage2D");
|
||||||
|
glad_glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)load("glCompressedTextureSubImage3D");
|
||||||
|
glad_glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC)load("glCopyTextureSubImage1D");
|
||||||
|
glad_glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC)load("glCopyTextureSubImage2D");
|
||||||
|
glad_glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC)load("glCopyTextureSubImage3D");
|
||||||
|
glad_glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC)load("glTextureParameterf");
|
||||||
|
glad_glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC)load("glTextureParameterfv");
|
||||||
|
glad_glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC)load("glTextureParameteri");
|
||||||
|
glad_glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC)load("glTextureParameterIiv");
|
||||||
|
glad_glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC)load("glTextureParameterIuiv");
|
||||||
|
glad_glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC)load("glTextureParameteriv");
|
||||||
|
glad_glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)load("glGenerateTextureMipmap");
|
||||||
|
glad_glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC)load("glBindTextureUnit");
|
||||||
|
glad_glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC)load("glGetTextureImage");
|
||||||
|
glad_glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)load("glGetCompressedTextureImage");
|
||||||
|
glad_glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC)load("glGetTextureLevelParameterfv");
|
||||||
|
glad_glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC)load("glGetTextureLevelParameteriv");
|
||||||
|
glad_glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC)load("glGetTextureParameterfv");
|
||||||
|
glad_glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC)load("glGetTextureParameterIiv");
|
||||||
|
glad_glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC)load("glGetTextureParameterIuiv");
|
||||||
|
glad_glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC)load("glGetTextureParameteriv");
|
||||||
|
glad_glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC)load("glCreateVertexArrays");
|
||||||
|
glad_glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC)load("glDisableVertexArrayAttrib");
|
||||||
|
glad_glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC)load("glEnableVertexArrayAttrib");
|
||||||
|
glad_glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC)load("glVertexArrayElementBuffer");
|
||||||
|
glad_glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC)load("glVertexArrayVertexBuffer");
|
||||||
|
glad_glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC)load("glVertexArrayVertexBuffers");
|
||||||
|
glad_glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC)load("glVertexArrayAttribBinding");
|
||||||
|
glad_glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC)load("glVertexArrayAttribFormat");
|
||||||
|
glad_glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC)load("glVertexArrayAttribIFormat");
|
||||||
|
glad_glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC)load("glVertexArrayAttribLFormat");
|
||||||
|
glad_glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC)load("glVertexArrayBindingDivisor");
|
||||||
|
glad_glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC)load("glGetVertexArrayiv");
|
||||||
|
glad_glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC)load("glGetVertexArrayIndexediv");
|
||||||
|
glad_glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC)load("glGetVertexArrayIndexed64iv");
|
||||||
|
glad_glCreateSamplers = (PFNGLCREATESAMPLERSPROC)load("glCreateSamplers");
|
||||||
|
glad_glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC)load("glCreateProgramPipelines");
|
||||||
|
glad_glCreateQueries = (PFNGLCREATEQUERIESPROC)load("glCreateQueries");
|
||||||
|
glad_glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC)load("glGetQueryBufferObjecti64v");
|
||||||
|
glad_glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC)load("glGetQueryBufferObjectiv");
|
||||||
|
glad_glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC)load("glGetQueryBufferObjectui64v");
|
||||||
|
glad_glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC)load("glGetQueryBufferObjectuiv");
|
||||||
|
glad_glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)load("glMemoryBarrierByRegion");
|
||||||
|
glad_glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC)load("glGetTextureSubImage");
|
||||||
|
glad_glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)load("glGetCompressedTextureSubImage");
|
||||||
|
glad_glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC)load("glGetGraphicsResetStatus");
|
||||||
|
glad_glGetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC)load("glGetnCompressedTexImage");
|
||||||
|
glad_glGetnTexImage = (PFNGLGETNTEXIMAGEPROC)load("glGetnTexImage");
|
||||||
|
glad_glGetnUniformdv = (PFNGLGETNUNIFORMDVPROC)load("glGetnUniformdv");
|
||||||
|
glad_glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC)load("glGetnUniformfv");
|
||||||
|
glad_glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC)load("glGetnUniformiv");
|
||||||
|
glad_glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC)load("glGetnUniformuiv");
|
||||||
|
glad_glReadnPixels = (PFNGLREADNPIXELSPROC)load("glReadnPixels");
|
||||||
|
glad_glGetnMapdv = (PFNGLGETNMAPDVPROC)load("glGetnMapdv");
|
||||||
|
glad_glGetnMapfv = (PFNGLGETNMAPFVPROC)load("glGetnMapfv");
|
||||||
|
glad_glGetnMapiv = (PFNGLGETNMAPIVPROC)load("glGetnMapiv");
|
||||||
|
glad_glGetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC)load("glGetnPixelMapfv");
|
||||||
|
glad_glGetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC)load("glGetnPixelMapuiv");
|
||||||
|
glad_glGetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC)load("glGetnPixelMapusv");
|
||||||
|
glad_glGetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC)load("glGetnPolygonStipple");
|
||||||
|
glad_glGetnColorTable = (PFNGLGETNCOLORTABLEPROC)load("glGetnColorTable");
|
||||||
|
glad_glGetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC)load("glGetnConvolutionFilter");
|
||||||
|
glad_glGetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC)load("glGetnSeparableFilter");
|
||||||
|
glad_glGetnHistogram = (PFNGLGETNHISTOGRAMPROC)load("glGetnHistogram");
|
||||||
|
glad_glGetnMinmax = (PFNGLGETNMINMAXPROC)load("glGetnMinmax");
|
||||||
|
glad_glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)load("glTextureBarrier");
|
||||||
|
}
|
||||||
|
static void load_GL_VERSION_4_6(GLADloadproc load) {
|
||||||
|
if(!GLAD_GL_VERSION_4_6) return;
|
||||||
|
glad_glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)load("glSpecializeShader");
|
||||||
|
glad_glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)load("glMultiDrawArraysIndirectCount");
|
||||||
|
glad_glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)load("glMultiDrawElementsIndirectCount");
|
||||||
|
glad_glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)load("glPolygonOffsetClamp");
|
||||||
|
}
|
||||||
static void load_GL_ARB_buffer_storage(GLADloadproc load) {
|
static void load_GL_ARB_buffer_storage(GLADloadproc load) {
|
||||||
if(!GLAD_GL_ARB_buffer_storage) return;
|
if(!GLAD_GL_ARB_buffer_storage) return;
|
||||||
glad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load("glBufferStorage");
|
glad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load("glBufferStorage");
|
||||||
@ -1698,6 +1859,8 @@ static int find_extensionsGL(void) {
|
|||||||
if (!get_exts()) return 0;
|
if (!get_exts()) return 0;
|
||||||
GLAD_GL_ARB_buffer_storage = has_ext("GL_ARB_buffer_storage");
|
GLAD_GL_ARB_buffer_storage = has_ext("GL_ARB_buffer_storage");
|
||||||
GLAD_GL_ARB_direct_state_access = has_ext("GL_ARB_direct_state_access");
|
GLAD_GL_ARB_direct_state_access = has_ext("GL_ARB_direct_state_access");
|
||||||
|
GLAD_GL_ARB_texture_compression_bptc = has_ext("GL_ARB_texture_compression_bptc");
|
||||||
|
GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
|
||||||
free_exts();
|
free_exts();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1755,9 +1918,11 @@ static void find_coreGL(void) {
|
|||||||
GLAD_GL_VERSION_4_2 = (major == 4 && minor >= 2) || major > 4;
|
GLAD_GL_VERSION_4_2 = (major == 4 && minor >= 2) || major > 4;
|
||||||
GLAD_GL_VERSION_4_3 = (major == 4 && minor >= 3) || major > 4;
|
GLAD_GL_VERSION_4_3 = (major == 4 && minor >= 3) || major > 4;
|
||||||
GLAD_GL_VERSION_4_4 = (major == 4 && minor >= 4) || major > 4;
|
GLAD_GL_VERSION_4_4 = (major == 4 && minor >= 4) || major > 4;
|
||||||
if (GLVersion.major > 4 || (GLVersion.major >= 4 && GLVersion.minor >= 4)) {
|
GLAD_GL_VERSION_4_5 = (major == 4 && minor >= 5) || major > 4;
|
||||||
|
GLAD_GL_VERSION_4_6 = (major == 4 && minor >= 6) || major > 4;
|
||||||
|
if (GLVersion.major > 4 || (GLVersion.major >= 4 && GLVersion.minor >= 6)) {
|
||||||
max_loaded_major = 4;
|
max_loaded_major = 4;
|
||||||
max_loaded_minor = 4;
|
max_loaded_minor = 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1784,6 +1949,8 @@ int gladLoadGLLoader(GLADloadproc load) {
|
|||||||
load_GL_VERSION_4_2(load);
|
load_GL_VERSION_4_2(load);
|
||||||
load_GL_VERSION_4_3(load);
|
load_GL_VERSION_4_3(load);
|
||||||
load_GL_VERSION_4_4(load);
|
load_GL_VERSION_4_4(load);
|
||||||
|
load_GL_VERSION_4_5(load);
|
||||||
|
load_GL_VERSION_4_6(load);
|
||||||
|
|
||||||
if (!find_extensionsGL()) return 0;
|
if (!find_extensionsGL()) return 0;
|
||||||
load_GL_ARB_buffer_storage(load);
|
load_GL_ARB_buffer_storage(load);
|
||||||
@ -2169,6 +2336,7 @@ static int find_extensionsGLES2(void) {
|
|||||||
if (!get_exts()) return 0;
|
if (!get_exts()) return 0;
|
||||||
GLAD_GL_EXT_buffer_storage = has_ext("GL_EXT_buffer_storage");
|
GLAD_GL_EXT_buffer_storage = has_ext("GL_EXT_buffer_storage");
|
||||||
GLAD_GL_EXT_clip_cull_distance = has_ext("GL_EXT_clip_cull_distance");
|
GLAD_GL_EXT_clip_cull_distance = has_ext("GL_EXT_clip_cull_distance");
|
||||||
|
GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
|
||||||
free_exts();
|
free_exts();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
14
externals/libspng/CMakeLists.txt
vendored
Normal file
14
externals/libspng/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
add_library(spng STATIC spng.h spng.c)
|
||||||
|
target_compile_definitions(spng PUBLIC SPNG_STATIC)
|
||||||
|
target_include_directories(spng PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
target_link_libraries(spng PRIVATE ZLIB::ZLIB)
|
||||||
|
|
||||||
|
# Enable SSE4.1 on x64
|
||||||
|
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
|
target_compile_definitions(spng PRIVATE SPNG_SSE=4)
|
||||||
|
if (NOT MSVC)
|
||||||
|
target_compile_options(spng PRIVATE -msse4.1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(spng::spng ALIAS spng)
|
6979
externals/libspng/spng.c
vendored
Normal file
6979
externals/libspng/spng.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
537
externals/libspng/spng.h
vendored
Normal file
537
externals/libspng/spng.h
vendored
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
#ifndef SPNG_H
|
||||||
|
#define SPNG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC)
|
||||||
|
#if defined(SPNG__BUILD)
|
||||||
|
#define SPNG_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define SPNG_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define SPNG_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define SPNG_CDECL __cdecl
|
||||||
|
#else
|
||||||
|
#define SPNG_CDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define SPNG_VERSION_MAJOR 0
|
||||||
|
#define SPNG_VERSION_MINOR 7
|
||||||
|
#define SPNG_VERSION_PATCH 3
|
||||||
|
|
||||||
|
enum spng_errno
|
||||||
|
{
|
||||||
|
SPNG_IO_ERROR = -2,
|
||||||
|
SPNG_IO_EOF = -1,
|
||||||
|
SPNG_OK = 0,
|
||||||
|
SPNG_EINVAL,
|
||||||
|
SPNG_EMEM,
|
||||||
|
SPNG_EOVERFLOW,
|
||||||
|
SPNG_ESIGNATURE,
|
||||||
|
SPNG_EWIDTH,
|
||||||
|
SPNG_EHEIGHT,
|
||||||
|
SPNG_EUSER_WIDTH,
|
||||||
|
SPNG_EUSER_HEIGHT,
|
||||||
|
SPNG_EBIT_DEPTH,
|
||||||
|
SPNG_ECOLOR_TYPE,
|
||||||
|
SPNG_ECOMPRESSION_METHOD,
|
||||||
|
SPNG_EFILTER_METHOD,
|
||||||
|
SPNG_EINTERLACE_METHOD,
|
||||||
|
SPNG_EIHDR_SIZE,
|
||||||
|
SPNG_ENOIHDR,
|
||||||
|
SPNG_ECHUNK_POS,
|
||||||
|
SPNG_ECHUNK_SIZE,
|
||||||
|
SPNG_ECHUNK_CRC,
|
||||||
|
SPNG_ECHUNK_TYPE,
|
||||||
|
SPNG_ECHUNK_UNKNOWN_CRITICAL,
|
||||||
|
SPNG_EDUP_PLTE,
|
||||||
|
SPNG_EDUP_CHRM,
|
||||||
|
SPNG_EDUP_GAMA,
|
||||||
|
SPNG_EDUP_ICCP,
|
||||||
|
SPNG_EDUP_SBIT,
|
||||||
|
SPNG_EDUP_SRGB,
|
||||||
|
SPNG_EDUP_BKGD,
|
||||||
|
SPNG_EDUP_HIST,
|
||||||
|
SPNG_EDUP_TRNS,
|
||||||
|
SPNG_EDUP_PHYS,
|
||||||
|
SPNG_EDUP_TIME,
|
||||||
|
SPNG_EDUP_OFFS,
|
||||||
|
SPNG_EDUP_EXIF,
|
||||||
|
SPNG_ECHRM,
|
||||||
|
SPNG_EPLTE_IDX,
|
||||||
|
SPNG_ETRNS_COLOR_TYPE,
|
||||||
|
SPNG_ETRNS_NO_PLTE,
|
||||||
|
SPNG_EGAMA,
|
||||||
|
SPNG_EICCP_NAME,
|
||||||
|
SPNG_EICCP_COMPRESSION_METHOD,
|
||||||
|
SPNG_ESBIT,
|
||||||
|
SPNG_ESRGB,
|
||||||
|
SPNG_ETEXT,
|
||||||
|
SPNG_ETEXT_KEYWORD,
|
||||||
|
SPNG_EZTXT,
|
||||||
|
SPNG_EZTXT_COMPRESSION_METHOD,
|
||||||
|
SPNG_EITXT,
|
||||||
|
SPNG_EITXT_COMPRESSION_FLAG,
|
||||||
|
SPNG_EITXT_COMPRESSION_METHOD,
|
||||||
|
SPNG_EITXT_LANG_TAG,
|
||||||
|
SPNG_EITXT_TRANSLATED_KEY,
|
||||||
|
SPNG_EBKGD_NO_PLTE,
|
||||||
|
SPNG_EBKGD_PLTE_IDX,
|
||||||
|
SPNG_EHIST_NO_PLTE,
|
||||||
|
SPNG_EPHYS,
|
||||||
|
SPNG_ESPLT_NAME,
|
||||||
|
SPNG_ESPLT_DUP_NAME,
|
||||||
|
SPNG_ESPLT_DEPTH,
|
||||||
|
SPNG_ETIME,
|
||||||
|
SPNG_EOFFS,
|
||||||
|
SPNG_EEXIF,
|
||||||
|
SPNG_EIDAT_TOO_SHORT,
|
||||||
|
SPNG_EIDAT_STREAM,
|
||||||
|
SPNG_EZLIB,
|
||||||
|
SPNG_EFILTER,
|
||||||
|
SPNG_EBUFSIZ,
|
||||||
|
SPNG_EIO,
|
||||||
|
SPNG_EOF,
|
||||||
|
SPNG_EBUF_SET,
|
||||||
|
SPNG_EBADSTATE,
|
||||||
|
SPNG_EFMT,
|
||||||
|
SPNG_EFLAGS,
|
||||||
|
SPNG_ECHUNKAVAIL,
|
||||||
|
SPNG_ENCODE_ONLY,
|
||||||
|
SPNG_EOI,
|
||||||
|
SPNG_ENOPLTE,
|
||||||
|
SPNG_ECHUNK_LIMITS,
|
||||||
|
SPNG_EZLIB_INIT,
|
||||||
|
SPNG_ECHUNK_STDLEN,
|
||||||
|
SPNG_EINTERNAL,
|
||||||
|
SPNG_ECTXTYPE,
|
||||||
|
SPNG_ENOSRC,
|
||||||
|
SPNG_ENODST,
|
||||||
|
SPNG_EOPSTATE,
|
||||||
|
SPNG_ENOTFINAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_text_type
|
||||||
|
{
|
||||||
|
SPNG_TEXT = 1,
|
||||||
|
SPNG_ZTXT = 2,
|
||||||
|
SPNG_ITXT = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_color_type
|
||||||
|
{
|
||||||
|
SPNG_COLOR_TYPE_GRAYSCALE = 0,
|
||||||
|
SPNG_COLOR_TYPE_TRUECOLOR = 2,
|
||||||
|
SPNG_COLOR_TYPE_INDEXED = 3,
|
||||||
|
SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4,
|
||||||
|
SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_filter
|
||||||
|
{
|
||||||
|
SPNG_FILTER_NONE = 0,
|
||||||
|
SPNG_FILTER_SUB = 1,
|
||||||
|
SPNG_FILTER_UP = 2,
|
||||||
|
SPNG_FILTER_AVERAGE = 3,
|
||||||
|
SPNG_FILTER_PAETH = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_filter_choice
|
||||||
|
{
|
||||||
|
SPNG_DISABLE_FILTERING = 0,
|
||||||
|
SPNG_FILTER_CHOICE_NONE = 8,
|
||||||
|
SPNG_FILTER_CHOICE_SUB = 16,
|
||||||
|
SPNG_FILTER_CHOICE_UP = 32,
|
||||||
|
SPNG_FILTER_CHOICE_AVG = 64,
|
||||||
|
SPNG_FILTER_CHOICE_PAETH = 128,
|
||||||
|
SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_interlace_method
|
||||||
|
{
|
||||||
|
SPNG_INTERLACE_NONE = 0,
|
||||||
|
SPNG_INTERLACE_ADAM7 = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Channels are always in byte-order */
|
||||||
|
enum spng_format
|
||||||
|
{
|
||||||
|
SPNG_FMT_RGBA8 = 1,
|
||||||
|
SPNG_FMT_RGBA16 = 2,
|
||||||
|
SPNG_FMT_RGB8 = 4,
|
||||||
|
|
||||||
|
/* Partially implemented, see documentation */
|
||||||
|
SPNG_FMT_GA8 = 16,
|
||||||
|
SPNG_FMT_GA16 = 32,
|
||||||
|
SPNG_FMT_G8 = 64,
|
||||||
|
|
||||||
|
/* No conversion or scaling */
|
||||||
|
SPNG_FMT_PNG = 256,
|
||||||
|
SPNG_FMT_RAW = 512 /* big-endian (everything else is host-endian) */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_ctx_flags
|
||||||
|
{
|
||||||
|
SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */
|
||||||
|
SPNG_CTX_ENCODER = 2 /* Create an encoder context */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_decode_flags
|
||||||
|
{
|
||||||
|
SPNG_DECODE_USE_TRNS = 1, /* Deprecated */
|
||||||
|
SPNG_DECODE_USE_GAMA = 2, /* Deprecated */
|
||||||
|
SPNG_DECODE_USE_SBIT = 8, /* Undocumented */
|
||||||
|
|
||||||
|
SPNG_DECODE_TRNS = 1, /* Apply transparency */
|
||||||
|
SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */
|
||||||
|
SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_crc_action
|
||||||
|
{
|
||||||
|
/* Default for critical chunks */
|
||||||
|
SPNG_CRC_ERROR = 0,
|
||||||
|
|
||||||
|
/* Discard chunk, invalid for critical chunks.
|
||||||
|
Since v0.6.2: default for ancillary chunks */
|
||||||
|
SPNG_CRC_DISCARD = 1,
|
||||||
|
|
||||||
|
/* Ignore and don't calculate checksum.
|
||||||
|
Since v0.6.2: also ignores checksums in DEFLATE streams */
|
||||||
|
SPNG_CRC_USE = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_encode_flags
|
||||||
|
{
|
||||||
|
SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */
|
||||||
|
SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_ihdr
|
||||||
|
{
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint8_t bit_depth;
|
||||||
|
uint8_t color_type;
|
||||||
|
uint8_t compression_method;
|
||||||
|
uint8_t filter_method;
|
||||||
|
uint8_t interlace_method;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_plte_entry
|
||||||
|
{
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
|
||||||
|
uint8_t alpha; /* Reserved for internal use */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_plte
|
||||||
|
{
|
||||||
|
uint32_t n_entries;
|
||||||
|
struct spng_plte_entry entries[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_trns
|
||||||
|
{
|
||||||
|
uint16_t gray;
|
||||||
|
|
||||||
|
uint16_t red;
|
||||||
|
uint16_t green;
|
||||||
|
uint16_t blue;
|
||||||
|
|
||||||
|
uint32_t n_type3_entries;
|
||||||
|
uint8_t type3_alpha[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_chrm_int
|
||||||
|
{
|
||||||
|
uint32_t white_point_x;
|
||||||
|
uint32_t white_point_y;
|
||||||
|
uint32_t red_x;
|
||||||
|
uint32_t red_y;
|
||||||
|
uint32_t green_x;
|
||||||
|
uint32_t green_y;
|
||||||
|
uint32_t blue_x;
|
||||||
|
uint32_t blue_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_chrm
|
||||||
|
{
|
||||||
|
double white_point_x;
|
||||||
|
double white_point_y;
|
||||||
|
double red_x;
|
||||||
|
double red_y;
|
||||||
|
double green_x;
|
||||||
|
double green_y;
|
||||||
|
double blue_x;
|
||||||
|
double blue_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_iccp
|
||||||
|
{
|
||||||
|
char profile_name[80];
|
||||||
|
size_t profile_len;
|
||||||
|
char *profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_sbit
|
||||||
|
{
|
||||||
|
uint8_t grayscale_bits;
|
||||||
|
uint8_t red_bits;
|
||||||
|
uint8_t green_bits;
|
||||||
|
uint8_t blue_bits;
|
||||||
|
uint8_t alpha_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_text
|
||||||
|
{
|
||||||
|
char keyword[80];
|
||||||
|
int type;
|
||||||
|
|
||||||
|
size_t length;
|
||||||
|
char *text;
|
||||||
|
|
||||||
|
uint8_t compression_flag; /* iTXt only */
|
||||||
|
uint8_t compression_method; /* iTXt, ztXt only */
|
||||||
|
char *language_tag; /* iTXt only */
|
||||||
|
char *translated_keyword; /* iTXt only */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_bkgd
|
||||||
|
{
|
||||||
|
uint16_t gray; /* Only for gray/gray alpha */
|
||||||
|
uint16_t red;
|
||||||
|
uint16_t green;
|
||||||
|
uint16_t blue;
|
||||||
|
uint16_t plte_index; /* Only for indexed color */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_hist
|
||||||
|
{
|
||||||
|
uint16_t frequency[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_phys
|
||||||
|
{
|
||||||
|
uint32_t ppu_x, ppu_y;
|
||||||
|
uint8_t unit_specifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_splt_entry
|
||||||
|
{
|
||||||
|
uint16_t red;
|
||||||
|
uint16_t green;
|
||||||
|
uint16_t blue;
|
||||||
|
uint16_t alpha;
|
||||||
|
uint16_t frequency;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_splt
|
||||||
|
{
|
||||||
|
char name[80];
|
||||||
|
uint8_t sample_depth;
|
||||||
|
uint32_t n_entries;
|
||||||
|
struct spng_splt_entry *entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_time
|
||||||
|
{
|
||||||
|
uint16_t year;
|
||||||
|
uint8_t month;
|
||||||
|
uint8_t day;
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minute;
|
||||||
|
uint8_t second;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_offs
|
||||||
|
{
|
||||||
|
int32_t x, y;
|
||||||
|
uint8_t unit_specifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_exif
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
char *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_chunk
|
||||||
|
{
|
||||||
|
size_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
uint8_t type[4];
|
||||||
|
uint32_t crc;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_location
|
||||||
|
{
|
||||||
|
SPNG_AFTER_IHDR = 1,
|
||||||
|
SPNG_AFTER_PLTE = 2,
|
||||||
|
SPNG_AFTER_IDAT = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_unknown_chunk
|
||||||
|
{
|
||||||
|
uint8_t type[4];
|
||||||
|
size_t length;
|
||||||
|
void *data;
|
||||||
|
enum spng_location location;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum spng_option
|
||||||
|
{
|
||||||
|
SPNG_KEEP_UNKNOWN_CHUNKS = 1,
|
||||||
|
|
||||||
|
SPNG_IMG_COMPRESSION_LEVEL,
|
||||||
|
SPNG_IMG_WINDOW_BITS,
|
||||||
|
SPNG_IMG_MEM_LEVEL,
|
||||||
|
SPNG_IMG_COMPRESSION_STRATEGY,
|
||||||
|
|
||||||
|
SPNG_TEXT_COMPRESSION_LEVEL,
|
||||||
|
SPNG_TEXT_WINDOW_BITS,
|
||||||
|
SPNG_TEXT_MEM_LEVEL,
|
||||||
|
SPNG_TEXT_COMPRESSION_STRATEGY,
|
||||||
|
|
||||||
|
SPNG_FILTER_CHOICE,
|
||||||
|
SPNG_CHUNK_COUNT_LIMIT,
|
||||||
|
SPNG_ENCODE_TO_BUFFER,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void* SPNG_CDECL spng_malloc_fn(size_t size);
|
||||||
|
typedef void* SPNG_CDECL spng_realloc_fn(void* ptr, size_t size);
|
||||||
|
typedef void* SPNG_CDECL spng_calloc_fn(size_t count, size_t size);
|
||||||
|
typedef void SPNG_CDECL spng_free_fn(void* ptr);
|
||||||
|
|
||||||
|
struct spng_alloc
|
||||||
|
{
|
||||||
|
spng_malloc_fn *malloc_fn;
|
||||||
|
spng_realloc_fn *realloc_fn;
|
||||||
|
spng_calloc_fn *calloc_fn;
|
||||||
|
spng_free_fn *free_fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spng_row_info
|
||||||
|
{
|
||||||
|
uint32_t scanline_idx;
|
||||||
|
uint32_t row_num; /* deinterlaced row index */
|
||||||
|
int pass;
|
||||||
|
uint8_t filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct spng_ctx spng_ctx;
|
||||||
|
|
||||||
|
typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length);
|
||||||
|
typedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length);
|
||||||
|
|
||||||
|
typedef int spng_rw_fn(spng_ctx *ctx, void *user, void *dst_src, size_t length);
|
||||||
|
|
||||||
|
SPNG_API spng_ctx *spng_ctx_new(int flags);
|
||||||
|
SPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags);
|
||||||
|
SPNG_API void spng_ctx_free(spng_ctx *ctx);
|
||||||
|
|
||||||
|
SPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size);
|
||||||
|
SPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user);
|
||||||
|
SPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file);
|
||||||
|
|
||||||
|
SPNG_API void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error);
|
||||||
|
|
||||||
|
SPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height);
|
||||||
|
SPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height);
|
||||||
|
|
||||||
|
SPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size);
|
||||||
|
SPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size);
|
||||||
|
|
||||||
|
SPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary);
|
||||||
|
|
||||||
|
SPNG_API int spng_set_option(spng_ctx *ctx, enum spng_option option, int value);
|
||||||
|
SPNG_API int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value);
|
||||||
|
|
||||||
|
SPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len);
|
||||||
|
|
||||||
|
/* Decode */
|
||||||
|
SPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags);
|
||||||
|
|
||||||
|
/* Progressive decode */
|
||||||
|
SPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len);
|
||||||
|
SPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len);
|
||||||
|
SPNG_API int spng_decode_chunks(spng_ctx *ctx);
|
||||||
|
|
||||||
|
/* Encode/decode */
|
||||||
|
SPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info);
|
||||||
|
|
||||||
|
/* Encode */
|
||||||
|
SPNG_API int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags);
|
||||||
|
|
||||||
|
/* Progressive encode */
|
||||||
|
SPNG_API int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len);
|
||||||
|
SPNG_API int spng_encode_row(spng_ctx *ctx, const void *row, size_t len);
|
||||||
|
SPNG_API int spng_encode_chunks(spng_ctx *ctx);
|
||||||
|
|
||||||
|
SPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
|
||||||
|
SPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte);
|
||||||
|
SPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns);
|
||||||
|
SPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
|
||||||
|
SPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
|
||||||
|
SPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma);
|
||||||
|
SPNG_API int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int);
|
||||||
|
SPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
|
||||||
|
SPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
|
||||||
|
SPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent);
|
||||||
|
SPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text);
|
||||||
|
SPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
|
||||||
|
SPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist);
|
||||||
|
SPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys);
|
||||||
|
SPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt);
|
||||||
|
SPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time);
|
||||||
|
SPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks);
|
||||||
|
|
||||||
|
/* Official extensions */
|
||||||
|
SPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs);
|
||||||
|
SPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif);
|
||||||
|
|
||||||
|
|
||||||
|
SPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
|
||||||
|
SPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte);
|
||||||
|
SPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns);
|
||||||
|
SPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
|
||||||
|
SPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
|
||||||
|
SPNG_API int spng_set_gama(spng_ctx *ctx, double gamma);
|
||||||
|
SPNG_API int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma);
|
||||||
|
SPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
|
||||||
|
SPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
|
||||||
|
SPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent);
|
||||||
|
SPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text);
|
||||||
|
SPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
|
||||||
|
SPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist);
|
||||||
|
SPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys);
|
||||||
|
SPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt);
|
||||||
|
SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time);
|
||||||
|
SPNG_API int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks);
|
||||||
|
|
||||||
|
/* Official extensions */
|
||||||
|
SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs);
|
||||||
|
SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif);
|
||||||
|
|
||||||
|
|
||||||
|
SPNG_API const char *spng_strerror(int err);
|
||||||
|
SPNG_API const char *spng_version_string(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SPNG_H */
|
7
externals/lodepng/CMakeLists.txt
vendored
7
externals/lodepng/CMakeLists.txt
vendored
@ -1,7 +0,0 @@
|
|||||||
add_library(lodepng
|
|
||||||
lodepng/lodepng.cpp
|
|
||||||
lodepng/lodepng.h
|
|
||||||
)
|
|
||||||
|
|
||||||
create_target_directory_groups(lodepng)
|
|
||||||
target_include_directories(lodepng INTERFACE lodepng)
|
|
1
externals/lodepng/lodepng
vendored
1
externals/lodepng/lodepng
vendored
Submodule externals/lodepng/lodepng deleted from 18964554bc
2
externals/vulkan-headers
vendored
2
externals/vulkan-headers
vendored
Submodule externals/vulkan-headers updated: 98f440ce68...a49166a89a
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: 48457bfa0d...a1ac3750f9
17
externals/zlib-ng/CMakeLists.txt
vendored
Normal file
17
externals/zlib-ng/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
set(ZLIB_ENABLE_TESTS OFF)
|
||||||
|
set(ZLIBNG_ENABLE_TESTS OFF)
|
||||||
|
set(WITH_GZFILEOP OFF)
|
||||||
|
set(WITH_GTEST OFF)
|
||||||
|
set(ZLIB_COMPAT ON)
|
||||||
|
set(SKIP_INSTALL_ALL ON)
|
||||||
|
|
||||||
|
option(BUILD_SHARED_LIBS "Build shared library" OFF)
|
||||||
|
|
||||||
|
add_subdirectory(zlib-ng)
|
||||||
|
|
||||||
|
# Set ZLIB variables for find_package used by other projects
|
||||||
|
set(ZLIB_INCLUDE_DIR ${CMAKE_BINARY_DIR}/zlib-ng CACHE STRING "Path to zlib include directory")
|
||||||
|
set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "Path to zlib library")
|
||||||
|
|
||||||
|
# Setup zlib alias project so FindZLIB doesn't recreate it
|
||||||
|
add_library(ZLIB::ZLIB ALIAS zlib)
|
1
externals/zlib-ng/zlib-ng
vendored
Submodule
1
externals/zlib-ng/zlib-ng
vendored
Submodule
Submodule externals/zlib-ng/zlib-ng added at c970422caa
@ -18,21 +18,12 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
|
||||||
// This is important as it will run lint but not abort on error
|
|
||||||
// Lint has some overly obnoxious "errors" that should really be warnings
|
|
||||||
abortOnError false
|
|
||||||
|
|
||||||
//Uncomment disable lines for test builds...
|
|
||||||
//disable 'MissingTranslation'bin
|
|
||||||
//disable 'ExtraTranslation'
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO If this is ever modified, change application_id in strings.xml
|
// TODO If this is ever modified, change application_id in strings.xml
|
||||||
applicationId "org.citra.citra_emu"
|
applicationId "org.citra.citra_emu"
|
||||||
minSdkVersion 28
|
minSdkVersion 28
|
||||||
targetSdkVersion 29
|
targetSdkVersion 31
|
||||||
versionCode autoVersion
|
versionCode autoVersion
|
||||||
versionName getVersion()
|
versionName getVersion()
|
||||||
ndk.abiFilters abiFilter
|
ndk.abiFilters abiFilter
|
||||||
@ -96,10 +87,14 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
version "3.18.1"
|
version "3.22.1"
|
||||||
path "../../../CMakeLists.txt"
|
path "../../../CMakeLists.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lint {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
namespace 'org.citra.citra_emu'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
@ -107,7 +102,8 @@ android {
|
|||||||
arguments "-DENABLE_QT=0", // Don't use QT
|
arguments "-DENABLE_QT=0", // Don't use QT
|
||||||
"-DENABLE_SDL2=0", // Don't use SDL
|
"-DENABLE_SDL2=0", // Don't use SDL
|
||||||
"-DENABLE_WEB_SERVICE=0", // Don't use telemetry
|
"-DENABLE_WEB_SERVICE=0", // Don't use telemetry
|
||||||
"-DANDROID_ARM_NEON=true" // cryptopp requires Neon to work
|
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
||||||
|
"-DBUNDLE_SPEEX=ON"
|
||||||
|
|
||||||
abiFilters abiFilter
|
abiFilters abiFilter
|
||||||
}
|
}
|
||||||
@ -116,22 +112,25 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation "androidx.activity:activity:1.5.1"
|
||||||
|
implementation "androidx.fragment:fragment:1.5.5"
|
||||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.3.4'
|
implementation 'androidx.exifinterface:exifinterface:1.3.4'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
|
implementation "androidx.documentfile:documentfile:1.0.1"
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
|
||||||
implementation 'androidx.fragment:fragment:1.5.3'
|
implementation 'androidx.fragment:fragment:1.5.3'
|
||||||
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
|
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
|
||||||
implementation 'com.google.android.material:material:1.6.1'
|
implementation 'com.google.android.material:material:1.6.1'
|
||||||
|
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||||
|
|
||||||
// For loading huge screenshots from the disk.
|
// For loading huge screenshots from the disk.
|
||||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||||
|
|
||||||
// Allows FRP-style asynchronous operations in Android.
|
// Allows FRP-style asynchronous operations in Android.
|
||||||
implementation 'io.reactivex:rxandroid:1.2.1'
|
implementation 'io.reactivex:rxandroid:1.2.1'
|
||||||
implementation 'com.nononsenseapps:filepicker:4.2.1'
|
|
||||||
implementation 'org.ini4j:ini4j:0.5.4'
|
implementation 'org.ini4j:ini4j:0.5.4'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
|
||||||
@ -139,6 +138,10 @@ dependencies {
|
|||||||
|
|
||||||
// Please don't upgrade the billing library as the newer version is not GPL-compatible
|
// Please don't upgrade the billing library as the newer version is not GPL-compatible
|
||||||
implementation 'com.android.billingclient:billing:2.0.3'
|
implementation 'com.android.billingclient:billing:2.0.3'
|
||||||
|
|
||||||
|
// To use the androidx.test.core APIs
|
||||||
|
androidTestImplementation "androidx.test:core:1.5.0"
|
||||||
|
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
def getVersion() {
|
def getVersion() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.citra.citra_emu;
|
package org.citra.citra_emu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -19,7 +19,7 @@ public class ExampleInstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
public void useAppContext() {
|
public void useAppContext() {
|
||||||
// Context of the app under test.
|
// Context of the app under test.
|
||||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
Context appContext = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
assertEquals("org.citra.citra_emu", appContext.getPackageName());
|
assertEquals("org.citra.citra_emu", appContext.getPackageName());
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,35 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="org.citra.citra_emu">
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.touchscreen"
|
android:name="android.hardware.touchscreen"
|
||||||
android:required="false"/>
|
android:required="false"/>
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.gamepad"
|
android:name="android.hardware.gamepad"
|
||||||
android:required="false"/>
|
android:required="false"/>
|
||||||
|
<uses-feature
|
||||||
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
android:name="android.hardware.microphone"
|
||||||
|
android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.opengles.aep" android:required="true" />
|
<uses-feature
|
||||||
|
android:name="android.hardware.camera"
|
||||||
|
android:required="false"/>
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.camera.any"
|
android:name="android.hardware.camera.any"
|
||||||
android:required="false" />
|
android:required="false"/>
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.camera.autofocus"
|
||||||
|
android:required="false"/>
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.software.leanback"
|
||||||
|
android:required="false"/>
|
||||||
|
|
||||||
|
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||||
|
<uses-feature android:name="android.hardware.opengles.aep" android:required="true" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.citra.citra_emu.CitraApplication"
|
android:name="org.citra.citra_emu.CitraApplication"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
@ -35,48 +43,38 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.citra.citra_emu.ui.main.MainActivity"
|
android:name="org.citra.citra_emu.ui.main.MainActivity"
|
||||||
android:theme="@style/CitraBase"
|
android:theme="@style/Theme.Citra.Splash.Main"
|
||||||
|
android:exported="true"
|
||||||
android:resizeableActivity="false">
|
android:resizeableActivity="false">
|
||||||
|
|
||||||
<!-- This intentfilter marks this Activity as the one that gets launched from Home screen. -->
|
<!-- This intentfilter marks this Activity as the one that gets launched from Home screen. -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.citra.citra_emu.features.settings.ui.SettingsActivity"
|
android:name="org.citra.citra_emu.features.settings.ui.SettingsActivity"
|
||||||
android:configChanges="orientation|screenSize|uiMode"
|
android:configChanges="orientation|screenSize|uiMode"
|
||||||
android:theme="@style/CitraSettingsBase"
|
android:theme="@style/Theme.Citra.Main"
|
||||||
android:label="@string/preferences_settings"/>
|
android:label="@string/preferences_settings"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.citra.citra_emu.activities.EmulationActivity"
|
android:name="org.citra.citra_emu.activities.EmulationActivity"
|
||||||
android:resizeableActivity="false"
|
android:resizeableActivity="false"
|
||||||
android:theme="@style/CitraEmulationBase"
|
android:theme="@style/Theme.Citra.Main"
|
||||||
android:launchMode="singleTop"/>
|
android:launchMode="singleTop"/>
|
||||||
|
|
||||||
<service android:name="org.citra.citra_emu.utils.ForegroundService"/>
|
<service android:name="org.citra.citra_emu.utils.ForegroundService"/>
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name="org.citra.citra_emu.activities.CustomFilePickerActivity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/FilePickerTheme">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.GET_CONTENT" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.citra.citra_emu.features.cheats.ui.CheatsActivity"
|
android:name="org.citra.citra_emu.features.cheats.ui.CheatsActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/CitraSettingsBase"
|
android:theme="@style/Theme.Citra.Main"
|
||||||
android:label="@string/cheats"/>
|
android:label="@string/cheats"/>
|
||||||
|
|
||||||
<service android:name="org.citra.citra_emu.utils.DirectoryInitialization"/>
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="org.citra.citra_emu.model.GameProvider"
|
android:name="org.citra.citra_emu.model.GameProvider"
|
||||||
@ -84,16 +82,6 @@
|
|||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="${applicationId}.filesprovider"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/nnf_provider_paths" />
|
|
||||||
</provider>
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -12,10 +12,12 @@ import android.os.Build;
|
|||||||
|
|
||||||
import org.citra.citra_emu.model.GameDatabase;
|
import org.citra.citra_emu.model.GameDatabase;
|
||||||
import org.citra.citra_emu.utils.DirectoryInitialization;
|
import org.citra.citra_emu.utils.DirectoryInitialization;
|
||||||
|
import org.citra.citra_emu.utils.DocumentsTree;
|
||||||
import org.citra.citra_emu.utils.PermissionsHandler;
|
import org.citra.citra_emu.utils.PermissionsHandler;
|
||||||
|
|
||||||
public class CitraApplication extends Application {
|
public class CitraApplication extends Application {
|
||||||
public static GameDatabase databaseHelper;
|
public static GameDatabase databaseHelper;
|
||||||
|
public static DocumentsTree documentsTree;
|
||||||
private static CitraApplication application;
|
private static CitraApplication application;
|
||||||
|
|
||||||
private void createNotificationChannel() {
|
private void createNotificationChannel() {
|
||||||
@ -39,6 +41,7 @@ public class CitraApplication extends Application {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
application = this;
|
application = this;
|
||||||
|
documentsTree = new DocumentsTree();
|
||||||
|
|
||||||
if (PermissionsHandler.hasWriteAccess(getApplicationContext())) {
|
if (PermissionsHandler.hasWriteAccess(getApplicationContext())) {
|
||||||
DirectoryInitialization.start(getApplicationContext());
|
DirectoryInitialization.start(getApplicationContext());
|
||||||
|
@ -28,6 +28,7 @@ import androidx.fragment.app.DialogFragment;
|
|||||||
import org.citra.citra_emu.activities.EmulationActivity;
|
import org.citra.citra_emu.activities.EmulationActivity;
|
||||||
import org.citra.citra_emu.applets.SoftwareKeyboard;
|
import org.citra.citra_emu.applets.SoftwareKeyboard;
|
||||||
import org.citra.citra_emu.utils.EmulationMenuSettings;
|
import org.citra.citra_emu.utils.EmulationMenuSettings;
|
||||||
|
import org.citra.citra_emu.utils.FileUtil;
|
||||||
import org.citra.citra_emu.utils.Log;
|
import org.citra.citra_emu.utils.Log;
|
||||||
import org.citra.citra_emu.utils.PermissionsHandler;
|
import org.citra.citra_emu.utils.PermissionsHandler;
|
||||||
|
|
||||||
@ -38,6 +39,8 @@ import java.util.Objects;
|
|||||||
import static android.Manifest.permission.CAMERA;
|
import static android.Manifest.permission.CAMERA;
|
||||||
import static android.Manifest.permission.RECORD_AUDIO;
|
import static android.Manifest.permission.RECORD_AUDIO;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class which contains methods that interact
|
* Class which contains methods that interact
|
||||||
* with the native side of the Citra code.
|
* with the native side of the Citra code.
|
||||||
@ -162,6 +165,10 @@ public final class NativeLibrary {
|
|||||||
// Create the config.ini file.
|
// Create the config.ini file.
|
||||||
public static native void CreateConfigFile();
|
public static native void CreateConfigFile();
|
||||||
|
|
||||||
|
public static native void CreateLogFile();
|
||||||
|
|
||||||
|
public static native void LogUserDirectory(String directory);
|
||||||
|
|
||||||
public static native int DefaultCPUCore();
|
public static native int DefaultCPUCore();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,7 +252,7 @@ public final class NativeLibrary {
|
|||||||
final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title"));
|
final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title"));
|
||||||
final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message"));
|
final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message"));
|
||||||
|
|
||||||
return new AlertDialog.Builder(emulationActivity)
|
return new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(R.string.continue_button, (dialog, which) -> {
|
.setPositiveButton(R.string.continue_button, (dialog, which) -> {
|
||||||
@ -260,11 +267,11 @@ public final class NativeLibrary {
|
|||||||
coreErrorAlertLock.notify();
|
coreErrorAlertLock.notify();
|
||||||
}
|
}
|
||||||
}).setOnDismissListener(dialog -> {
|
}).setOnDismissListener(dialog -> {
|
||||||
coreErrorAlertResult = true;
|
coreErrorAlertResult = true;
|
||||||
synchronized (coreErrorAlertLock) {
|
synchronized (coreErrorAlertLock) {
|
||||||
coreErrorAlertLock.notify();
|
coreErrorAlertLock.notify();
|
||||||
}
|
}
|
||||||
}).create();
|
}).create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +352,7 @@ public final class NativeLibrary {
|
|||||||
} else {
|
} else {
|
||||||
// Create object used for waiting.
|
// Create object used for waiting.
|
||||||
final Object lock = new Object();
|
final Object lock = new Object();
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(caption)
|
.setTitle(caption)
|
||||||
.setMessage(text);
|
.setMessage(text);
|
||||||
|
|
||||||
@ -427,7 +434,7 @@ public final class NativeLibrary {
|
|||||||
return alertPromptResult;
|
return alertPromptResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AlertDialog.Builder displayAlertPromptImpl(String caption, String text, int buttonConfig) {
|
public static MaterialAlertDialogBuilder displayAlertPromptImpl(String caption, String text, int buttonConfig) {
|
||||||
final EmulationActivity emulationActivity = sEmulationActivity.get();
|
final EmulationActivity emulationActivity = sEmulationActivity.get();
|
||||||
alertPromptResult = "";
|
alertPromptResult = "";
|
||||||
alertPromptButton = 0;
|
alertPromptButton = 0;
|
||||||
@ -444,7 +451,7 @@ public final class NativeLibrary {
|
|||||||
FrameLayout container = new FrameLayout(emulationActivity);
|
FrameLayout container = new FrameLayout(emulationActivity);
|
||||||
container.addView(alertPromptEditText);
|
container.addView(alertPromptEditText);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(caption)
|
.setTitle(caption)
|
||||||
.setView(container)
|
.setView(container)
|
||||||
.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
|
.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
|
||||||
@ -506,7 +513,7 @@ public final class NativeLibrary {
|
|||||||
captionId = R.string.loader_error_encrypted;
|
captionId = R.string.loader_error_encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(captionId)
|
.setTitle(captionId)
|
||||||
.setMessage(Html.fromHtml("Please follow the guides to redump your <a href=\"https://citra-emu.org/wiki/dumping-game-cartridges/\">game cartidges</a> or <a href=\"https://citra-emu.org/wiki/dumping-installed-titles/\">installed titles</a>.", Html.FROM_HTML_MODE_LEGACY))
|
.setMessage(Html.fromHtml("Please follow the guides to redump your <a href=\"https://citra-emu.org/wiki/dumping-game-cartridges/\">game cartidges</a> or <a href=\"https://citra-emu.org/wiki/dumping-installed-titles/\">installed titles</a>.", Html.FROM_HTML_MODE_LEGACY))
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> emulationActivity.finish())
|
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> emulationActivity.finish())
|
||||||
@ -663,4 +670,73 @@ public final class NativeLibrary {
|
|||||||
public static final int RELEASED = 0;
|
public static final int RELEASED = 0;
|
||||||
public static final int PRESSED = 1;
|
public static final int PRESSED = 1;
|
||||||
}
|
}
|
||||||
|
public static boolean createFile(String directory, String filename) {
|
||||||
|
if (FileUtil.isNativePath(directory)) {
|
||||||
|
return CitraApplication.documentsTree.createFile(directory, filename);
|
||||||
|
}
|
||||||
|
return FileUtil.createFile(CitraApplication.getAppContext(), directory, filename) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean createDir(String directory, String directoryName) {
|
||||||
|
if (FileUtil.isNativePath(directory)) {
|
||||||
|
return CitraApplication.documentsTree.createDir(directory, directoryName);
|
||||||
|
}
|
||||||
|
return FileUtil.createDir(CitraApplication.getAppContext(), directory, directoryName) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int openContentUri(String path, String openMode) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.openContentUri(path, openMode);
|
||||||
|
}
|
||||||
|
return FileUtil.openContentUri(CitraApplication.getAppContext(), path, openMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] getFilesName(String path) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.getFilesName(path);
|
||||||
|
}
|
||||||
|
return FileUtil.getFilesName(CitraApplication.getAppContext(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getSize(String path) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.getFileSize(path);
|
||||||
|
}
|
||||||
|
return FileUtil.getFileSize(CitraApplication.getAppContext(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean fileExists(String path) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.Exists(path);
|
||||||
|
}
|
||||||
|
return FileUtil.Exists(CitraApplication.getAppContext(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDirectory(String path) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.isDirectory(path);
|
||||||
|
}
|
||||||
|
return FileUtil.isDirectory(CitraApplication.getAppContext(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copyFile(String sourcePath, String destinationParentPath, String destinationFilename) {
|
||||||
|
if (FileUtil.isNativePath(sourcePath) && FileUtil.isNativePath(destinationParentPath)) {
|
||||||
|
return CitraApplication.documentsTree.copyFile(sourcePath, destinationParentPath, destinationFilename);
|
||||||
|
}
|
||||||
|
return FileUtil.copyFile(CitraApplication.getAppContext(), sourcePath, destinationParentPath, destinationFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean renameFile(String path, String destinationFilename) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.renameFile(path, destinationFilename);
|
||||||
|
}
|
||||||
|
return FileUtil.renameFile(CitraApplication.getAppContext(), path, destinationFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean deleteDocument(String path) {
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
|
return CitraApplication.documentsTree.deleteDocument(path);
|
||||||
|
}
|
||||||
|
return FileUtil.deleteDocument(CitraApplication.getAppContext(), path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
package org.citra.citra_emu.activities;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
|
||||||
|
|
||||||
import org.citra.citra_emu.fragments.CustomFilePickerFragment;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class CustomFilePickerActivity extends FilePickerActivity {
|
|
||||||
public static final String EXTRA_TITLE = "filepicker.intent.TITLE";
|
|
||||||
public static final String EXTRA_EXTENSIONS = "filepicker.intent.EXTENSIONS";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AbstractFilePickerFragment<File> getFragment(
|
|
||||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
|
||||||
final boolean allowCreateDir, final boolean allowExistingFile,
|
|
||||||
final boolean singleClick) {
|
|
||||||
CustomFilePickerFragment fragment = new CustomFilePickerFragment();
|
|
||||||
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/"
|
|
||||||
fragment.setArgs(
|
|
||||||
startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
|
|
||||||
mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick);
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
|
||||||
int title = intent == null ? 0 : intent.getIntExtra(EXTRA_TITLE, 0);
|
|
||||||
fragment.setTitle(title);
|
|
||||||
String allowedExtensions = intent == null ? "*" : intent.getStringExtra(EXTRA_EXTENSIONS);
|
|
||||||
fragment.setAllowedExtensions(allowedExtensions);
|
|
||||||
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,31 +5,35 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.SubMenu;
|
import android.view.SubMenu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.SeekBar;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultCallback;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.PopupMenu;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import org.citra.citra_emu.CitraApplication;
|
import org.citra.citra_emu.CitraApplication;
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
|
import org.citra.citra_emu.contracts.OpenFileResultContract;
|
||||||
import org.citra.citra_emu.features.cheats.ui.CheatsActivity;
|
import org.citra.citra_emu.features.cheats.ui.CheatsActivity;
|
||||||
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting;
|
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting;
|
||||||
import org.citra.citra_emu.features.settings.ui.SettingsActivity;
|
import org.citra.citra_emu.features.settings.ui.SettingsActivity;
|
||||||
@ -42,6 +46,7 @@ import org.citra.citra_emu.utils.EmulationMenuSettings;
|
|||||||
import org.citra.citra_emu.utils.FileBrowserHelper;
|
import org.citra.citra_emu.utils.FileBrowserHelper;
|
||||||
import org.citra.citra_emu.utils.FileUtil;
|
import org.citra.citra_emu.utils.FileUtil;
|
||||||
import org.citra.citra_emu.utils.ForegroundService;
|
import org.citra.citra_emu.utils.ForegroundService;
|
||||||
|
import org.citra.citra_emu.utils.ThemeUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -53,6 +58,9 @@ import static android.Manifest.permission.CAMERA;
|
|||||||
import static android.Manifest.permission.RECORD_AUDIO;
|
import static android.Manifest.permission.RECORD_AUDIO;
|
||||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.google.android.material.slider.Slider;
|
||||||
|
|
||||||
public final class EmulationActivity extends AppCompatActivity {
|
public final class EmulationActivity extends AppCompatActivity {
|
||||||
public static final String EXTRA_SELECTED_GAME = "SelectedGame";
|
public static final String EXTRA_SELECTED_GAME = "SelectedGame";
|
||||||
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
|
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
|
||||||
@ -74,11 +82,24 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 15;
|
public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 15;
|
||||||
public static final int MENU_ACTION_DPAD_SLIDE_ENABLE = 16;
|
public static final int MENU_ACTION_DPAD_SLIDE_ENABLE = 16;
|
||||||
public static final int MENU_ACTION_OPEN_CHEATS = 17;
|
public static final int MENU_ACTION_OPEN_CHEATS = 17;
|
||||||
|
public static final int MENU_ACTION_CLOSE_GAME = 18;
|
||||||
|
|
||||||
public static final int REQUEST_SELECT_AMIIBO = 2;
|
public static final int REQUEST_SELECT_AMIIBO = 2;
|
||||||
private static final int EMULATION_RUNNING_NOTIFICATION = 0x1000;
|
private static final int EMULATION_RUNNING_NOTIFICATION = 0x1000;
|
||||||
private static SparseIntArray buttonsActionsMap = new SparseIntArray();
|
private static SparseIntArray buttonsActionsMap = new SparseIntArray();
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<Boolean> mOpenFileLauncher =
|
||||||
|
registerForActivityResult(new OpenFileResultContract(), result -> {
|
||||||
|
if (result == null)
|
||||||
|
return;
|
||||||
|
String[] selectedFiles = FileBrowserHelper.getSelectedFiles(
|
||||||
|
result, getApplicationContext(), Collections.singletonList("bin"));
|
||||||
|
if (selectedFiles == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
onAmiiboSelected(selectedFiles[0]);
|
||||||
|
});
|
||||||
|
|
||||||
static {
|
static {
|
||||||
buttonsActionsMap.append(R.id.menu_emulation_edit_layout,
|
buttonsActionsMap.append(R.id.menu_emulation_edit_layout,
|
||||||
EmulationActivity.MENU_ACTION_EDIT_CONTROLS_PLACEMENT);
|
EmulationActivity.MENU_ACTION_EDIT_CONTROLS_PLACEMENT);
|
||||||
@ -114,9 +135,10 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
EmulationActivity.MENU_ACTION_DPAD_SLIDE_ENABLE);
|
EmulationActivity.MENU_ACTION_DPAD_SLIDE_ENABLE);
|
||||||
buttonsActionsMap
|
buttonsActionsMap
|
||||||
.append(R.id.menu_emulation_open_cheats, EmulationActivity.MENU_ACTION_OPEN_CHEATS);
|
.append(R.id.menu_emulation_open_cheats, EmulationActivity.MENU_ACTION_OPEN_CHEATS);
|
||||||
|
buttonsActionsMap
|
||||||
|
.append(R.id.menu_emulation_close_game, EmulationActivity.MENU_ACTION_CLOSE_GAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
private View mDecorView;
|
|
||||||
private EmulationFragment mEmulationFragment;
|
private EmulationFragment mEmulationFragment;
|
||||||
private SharedPreferences mPreferences;
|
private SharedPreferences mPreferences;
|
||||||
private ControllerMappingHelper mControllerMappingHelper;
|
private ControllerMappingHelper mControllerMappingHelper;
|
||||||
@ -145,6 +167,8 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
ThemeUtil.applyTheme(this);
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
@ -160,21 +184,9 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
mControllerMappingHelper = new ControllerMappingHelper();
|
mControllerMappingHelper = new ControllerMappingHelper();
|
||||||
|
|
||||||
// Get a handle to the Window containing the UI.
|
|
||||||
mDecorView = getWindow().getDecorView();
|
|
||||||
mDecorView.setOnSystemUiVisibilityChangeListener(visibility ->
|
|
||||||
{
|
|
||||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
|
||||||
// Go back to immersive fullscreen mode in 3s
|
|
||||||
Handler handler = new Handler(getMainLooper());
|
|
||||||
handler.postDelayed(this::enableFullscreenImmersive, 3000 /* 3s */);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Set these options now so that the SurfaceView the game renders into is the right size.
|
// Set these options now so that the SurfaceView the game renders into is the right size.
|
||||||
enableFullscreenImmersive();
|
enableFullscreenImmersive();
|
||||||
|
|
||||||
setTheme(R.style.CitraEmulationBase);
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_emulation);
|
setContentView(R.layout.activity_emulation);
|
||||||
|
|
||||||
// Find or create the EmulationFragment
|
// Find or create the EmulationFragment
|
||||||
@ -223,21 +235,12 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
NativeLibrary.PauseEmulation();
|
View anchor = findViewById(R.id.menu_anchor);
|
||||||
new AlertDialog.Builder(this)
|
PopupMenu popupMenu = new PopupMenu(this, anchor);
|
||||||
.setTitle(R.string.emulation_close_game)
|
onCreateOptionsMenu(popupMenu.getMenu(), popupMenu.getMenuInflater());
|
||||||
.setMessage(R.string.emulation_close_game_message)
|
updateSavestateMenuOptions(popupMenu.getMenu());
|
||||||
.setPositiveButton(android.R.string.yes, (dialogInterface, i) ->
|
popupMenu.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
||||||
{
|
popupMenu.show();
|
||||||
mEmulationFragment.stopEmulation();
|
|
||||||
finish();
|
|
||||||
})
|
|
||||||
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) ->
|
|
||||||
NativeLibrary.UnPauseEmulation())
|
|
||||||
.setOnCancelListener(dialogInterface ->
|
|
||||||
NativeLibrary.UnPauseEmulation())
|
|
||||||
.create()
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -246,7 +249,7 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
case NativeLibrary.REQUEST_CODE_NATIVE_CAMERA:
|
case NativeLibrary.REQUEST_CODE_NATIVE_CAMERA:
|
||||||
if (grantResults[0] != PackageManager.PERMISSION_GRANTED &&
|
if (grantResults[0] != PackageManager.PERMISSION_GRANTED &&
|
||||||
shouldShowRequestPermissionRationale(CAMERA)) {
|
shouldShowRequestPermissionRationale(CAMERA)) {
|
||||||
new AlertDialog.Builder(this)
|
new MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.camera)
|
.setTitle(R.string.camera)
|
||||||
.setMessage(R.string.camera_permission_needed)
|
.setMessage(R.string.camera_permission_needed)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
@ -257,7 +260,7 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
case NativeLibrary.REQUEST_CODE_NATIVE_MIC:
|
case NativeLibrary.REQUEST_CODE_NATIVE_MIC:
|
||||||
if (grantResults[0] != PackageManager.PERMISSION_GRANTED &&
|
if (grantResults[0] != PackageManager.PERMISSION_GRANTED &&
|
||||||
shouldShowRequestPermissionRationale(RECORD_AUDIO)) {
|
shouldShowRequestPermissionRationale(RECORD_AUDIO)) {
|
||||||
new AlertDialog.Builder(this)
|
new MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.microphone)
|
.setTitle(R.string.microphone)
|
||||||
.setMessage(R.string.microphone_permission_needed)
|
.setMessage(R.string.microphone_permission_needed)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
@ -271,21 +274,30 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onEmulationStarted() {
|
||||||
|
Toast.makeText(this, getString(R.string.emulation_menu_help), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
private void enableFullscreenImmersive() {
|
private void enableFullscreenImmersive() {
|
||||||
// It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
mDecorView.setSystemUiVisibility(
|
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||||
View.SYSTEM_UI_FLAG_IMMERSIVE);
|
View.SYSTEM_UI_FLAG_IMMERSIVE |
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
getMenuInflater().inflate(R.menu.menu_emulation, menu);
|
onCreateOptionsMenu(menu, getMenuInflater());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.menu_emulation, menu);
|
||||||
|
|
||||||
int layoutOptionMenuItem = R.id.menu_screen_layout_landscape;
|
int layoutOptionMenuItem = R.id.menu_screen_layout_landscape;
|
||||||
switch (EmulationMenuSettings.getLandscapeScreenLayout()) {
|
switch (EmulationMenuSettings.getLandscapeScreenLayout()) {
|
||||||
@ -306,8 +318,6 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
menu.findItem(R.id.menu_emulation_show_fps).setChecked(EmulationMenuSettings.getShowFps());
|
menu.findItem(R.id.menu_emulation_show_fps).setChecked(EmulationMenuSettings.getShowFps());
|
||||||
menu.findItem(R.id.menu_emulation_swap_screens).setChecked(EmulationMenuSettings.getSwapScreens());
|
menu.findItem(R.id.menu_emulation_swap_screens).setChecked(EmulationMenuSettings.getSwapScreens());
|
||||||
menu.findItem(R.id.menu_emulation_show_overlay).setChecked(EmulationMenuSettings.getShowOverlay());
|
menu.findItem(R.id.menu_emulation_show_overlay).setChecked(EmulationMenuSettings.getShowOverlay());
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisplaySavestateWarning() {
|
private void DisplaySavestateWarning() {
|
||||||
@ -320,7 +330,7 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
View view = inflater.inflate(R.layout.dialog_checkbox, null);
|
View view = inflater.inflate(R.layout.dialog_checkbox, null);
|
||||||
CheckBox checkBox = view.findViewById(R.id.checkBox);
|
CheckBox checkBox = view.findViewById(R.id.checkBox);
|
||||||
|
|
||||||
new AlertDialog.Builder(this)
|
new MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.savestate_warning_title)
|
.setTitle(R.string.savestate_warning_title)
|
||||||
.setMessage(R.string.savestate_warning_message)
|
.setMessage(R.string.savestate_warning_message)
|
||||||
.setView(view)
|
.setView(view)
|
||||||
@ -333,12 +343,16 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
super.onPrepareOptionsMenu(menu);
|
super.onPrepareOptionsMenu(menu);
|
||||||
|
updateSavestateMenuOptions(menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSavestateMenuOptions(Menu menu) {
|
||||||
final NativeLibrary.SavestateInfo[] savestates = NativeLibrary.GetSavestateInfo();
|
final NativeLibrary.SavestateInfo[] savestates = NativeLibrary.GetSavestateInfo();
|
||||||
if (savestates == null) {
|
if (savestates == null) {
|
||||||
menu.findItem(R.id.menu_emulation_save_state).setVisible(false);
|
menu.findItem(R.id.menu_emulation_save_state).setVisible(false);
|
||||||
menu.findItem(R.id.menu_emulation_load_state).setVisible(false);
|
menu.findItem(R.id.menu_emulation_load_state).setVisible(false);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
menu.findItem(R.id.menu_emulation_save_state).setVisible(true);
|
menu.findItem(R.id.menu_emulation_save_state).setVisible(true);
|
||||||
menu.findItem(R.id.menu_emulation_load_state).setVisible(true);
|
menu.findItem(R.id.menu_emulation_load_state).setVisible(true);
|
||||||
@ -367,7 +381,6 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
saveStateMenu.getItem(info.slot - 1).setTitle(text);
|
saveStateMenu.getItem(info.slot - 1).setTitle(text);
|
||||||
loadStateMenu.getItem(info.slot - 1).setTitle(text).setEnabled(true);
|
loadStateMenu.getItem(info.slot - 1).setTitle(text).setEnabled(true);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WrongConstant")
|
@SuppressWarnings("WrongConstant")
|
||||||
@ -456,9 +469,7 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MENU_ACTION_LOAD_AMIIBO:
|
case MENU_ACTION_LOAD_AMIIBO:
|
||||||
FileBrowserHelper.openFilePicker(this, REQUEST_SELECT_AMIIBO,
|
mOpenFileLauncher.launch(false);
|
||||||
R.string.select_amiibo,
|
|
||||||
Collections.singletonList("bin"), false);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MENU_ACTION_REMOVE_AMIIBO:
|
case MENU_ACTION_REMOVE_AMIIBO:
|
||||||
@ -480,6 +491,21 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
case MENU_ACTION_OPEN_CHEATS:
|
case MENU_ACTION_OPEN_CHEATS:
|
||||||
CheatsActivity.launch(this);
|
CheatsActivity.launch(this);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MENU_ACTION_CLOSE_GAME:
|
||||||
|
NativeLibrary.PauseEmulation();
|
||||||
|
new MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.emulation_close_game)
|
||||||
|
.setMessage(R.string.emulation_close_game_message)
|
||||||
|
.setPositiveButton(android.R.string.yes, (dialogInterface, i) ->
|
||||||
|
{
|
||||||
|
mEmulationFragment.stopEmulation();
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> NativeLibrary.UnPauseEmulation())
|
||||||
|
.setOnCancelListener(dialogInterface -> NativeLibrary.UnPauseEmulation())
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -536,20 +562,8 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent result) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent result) {
|
||||||
super.onActivityResult(requestCode, resultCode, result);
|
super.onActivityResult(requestCode, resultCode, result);
|
||||||
switch (requestCode) {
|
if (requestCode == StillImageCameraHelper.REQUEST_CAMERA_FILE_PICKER) {
|
||||||
case StillImageCameraHelper.REQUEST_CAMERA_FILE_PICKER:
|
StillImageCameraHelper.OnFilePickerResult(resultCode == RESULT_OK ? result : null);
|
||||||
StillImageCameraHelper.OnFilePickerResult(resultCode == RESULT_OK ? result : null);
|
|
||||||
break;
|
|
||||||
case REQUEST_SELECT_AMIIBO:
|
|
||||||
// If the user picked a file, as opposed to just backing out.
|
|
||||||
if (resultCode == MainActivity.RESULT_OK) {
|
|
||||||
String[] selectedFiles = FileBrowserHelper.getSelectedFiles(result);
|
|
||||||
if (selectedFiles == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
onAmiiboSelected(selectedFiles[0]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,11 +578,10 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
new AlertDialog.Builder(this)
|
new MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.amiibo_load_error)
|
.setTitle(R.string.amiibo_load_error)
|
||||||
.setMessage(R.string.amiibo_load_error_message)
|
.setMessage(R.string.amiibo_load_error_message)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
.create()
|
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,8 +593,6 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
private void toggleControls() {
|
private void toggleControls() {
|
||||||
final SharedPreferences.Editor editor = mPreferences.edit();
|
final SharedPreferences.Editor editor = mPreferences.edit();
|
||||||
boolean[] enabledButtons = new boolean[14];
|
boolean[] enabledButtons = new boolean[14];
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
builder.setTitle(R.string.emulation_toggle_controls);
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledButtons.length; i++) {
|
for (int i = 0; i < enabledButtons.length; i++) {
|
||||||
// Buttons that are disabled by default
|
// Buttons that are disabled by default
|
||||||
@ -596,63 +607,47 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, defaultValue);
|
enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, defaultValue);
|
||||||
}
|
}
|
||||||
builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons,
|
|
||||||
(dialog, indexSelected, isChecked) -> editor
|
|
||||||
.putBoolean("buttonToggle" + indexSelected, isChecked));
|
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
|
|
||||||
{
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
mEmulationFragment.refreshInputOverlay();
|
new MaterialAlertDialogBuilder(this)
|
||||||
});
|
.setTitle(R.string.emulation_toggle_controls)
|
||||||
|
.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons,
|
||||||
AlertDialog alertDialog = builder.create();
|
(dialog, indexSelected, isChecked) -> editor
|
||||||
alertDialog.show();
|
.putBoolean("buttonToggle" + indexSelected, isChecked))
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
|
||||||
|
{
|
||||||
|
editor.apply();
|
||||||
|
mEmulationFragment.refreshInputOverlay();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustScale() {
|
private void adjustScale() {
|
||||||
LayoutInflater inflater = LayoutInflater.from(this);
|
LayoutInflater inflater = LayoutInflater.from(this);
|
||||||
View view = inflater.inflate(R.layout.dialog_seekbar, null);
|
View view = inflater.inflate(R.layout.dialog_slider, null);
|
||||||
|
|
||||||
final SeekBar seekbar = view.findViewById(R.id.seekbar);
|
final Slider slider = view.findViewById(R.id.slider);
|
||||||
final TextView value = view.findViewById(R.id.text_value);
|
final TextView textValue = view.findViewById(R.id.text_value);
|
||||||
final TextView units = view.findViewById(R.id.text_units);
|
final TextView units = view.findViewById(R.id.text_units);
|
||||||
|
|
||||||
seekbar.setMax(150);
|
slider.setValueTo(150);
|
||||||
seekbar.setProgress(mPreferences.getInt("controlScale", 50));
|
slider.setValue(mPreferences.getInt("controlScale", 50));
|
||||||
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
slider.addOnChangeListener((slider1, progress, fromUser) -> {
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
textValue.setText(String.valueOf((int) progress + 50));
|
||||||
}
|
setControlScale((int) slider1.getValue());
|
||||||
|
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
||||||
value.setText(String.valueOf(progress + 50));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
||||||
setControlScale(seekbar.getProgress());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
value.setText(String.valueOf(seekbar.getProgress() + 50));
|
textValue.setText(String.valueOf((int) slider.getValue() + 50));
|
||||||
units.setText("%");
|
units.setText("%");
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
final int previousProgress = (int) slider.getValue();
|
||||||
builder.setTitle(R.string.emulation_control_scale);
|
|
||||||
builder.setView(view);
|
|
||||||
final int previousProgress = seekbar.getProgress();
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
|
|
||||||
setControlScale(previousProgress);
|
|
||||||
});
|
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
|
|
||||||
{
|
|
||||||
setControlScale(seekbar.getProgress());
|
|
||||||
});
|
|
||||||
builder.setNeutralButton(R.string.slider_default, (dialogInterface, i) -> {
|
|
||||||
setControlScale(50);
|
|
||||||
});
|
|
||||||
|
|
||||||
AlertDialog alertDialog = builder.create();
|
new MaterialAlertDialogBuilder(this)
|
||||||
alertDialog.show();
|
.setTitle(R.string.emulation_control_scale)
|
||||||
|
.setView(view)
|
||||||
|
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> setControlScale(previousProgress))
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> setControlScale((int) slider.getValue()))
|
||||||
|
.setNeutralButton(R.string.slider_default, (dialogInterface, i) -> setControlScale(50))
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setControlScale(int scale) {
|
private void setControlScale(int scale) {
|
||||||
@ -663,12 +658,10 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetOverlay() {
|
private void resetOverlay() {
|
||||||
new AlertDialog.Builder(this)
|
new MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(getString(R.string.emulation_touch_overlay_reset))
|
.setTitle(getString(R.string.emulation_touch_overlay_reset))
|
||||||
.setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mEmulationFragment.resetInputOverlay())
|
.setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mEmulationFragment.resetInputOverlay())
|
||||||
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
})
|
|
||||||
.create()
|
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@ package org.citra.citra_emu.adapters;
|
|||||||
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -12,20 +10,20 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
|
||||||
|
import org.citra.citra_emu.CitraApplication;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.activities.EmulationActivity;
|
import org.citra.citra_emu.activities.EmulationActivity;
|
||||||
import org.citra.citra_emu.model.GameDatabase;
|
import org.citra.citra_emu.model.GameDatabase;
|
||||||
import org.citra.citra_emu.ui.DividerItemDecoration;
|
import org.citra.citra_emu.utils.FileUtil;
|
||||||
import org.citra.citra_emu.utils.Log;
|
import org.citra.citra_emu.utils.Log;
|
||||||
import org.citra.citra_emu.utils.PicassoUtils;
|
import org.citra.citra_emu.utils.PicassoUtils;
|
||||||
import org.citra.citra_emu.viewholders.GameViewHolder;
|
import org.citra.citra_emu.viewholders.GameViewHolder;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,8 +86,14 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
|||||||
holder.textGameTitle.setText(mCursor.getString(GameDatabase.GAME_COLUMN_TITLE).replaceAll("[\\t\\n\\r]+", " "));
|
holder.textGameTitle.setText(mCursor.getString(GameDatabase.GAME_COLUMN_TITLE).replaceAll("[\\t\\n\\r]+", " "));
|
||||||
holder.textCompany.setText(mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY));
|
holder.textCompany.setText(mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY));
|
||||||
|
|
||||||
final Path gamePath = Paths.get(mCursor.getString(GameDatabase.GAME_COLUMN_PATH));
|
String filepath = mCursor.getString(GameDatabase.GAME_COLUMN_PATH);
|
||||||
holder.textFileName.setText(gamePath.getFileName().toString());
|
String filename;
|
||||||
|
if (FileUtil.isNativePath(filepath)) {
|
||||||
|
filename = CitraApplication.documentsTree.getFilename(filepath);
|
||||||
|
} else {
|
||||||
|
filename = FileUtil.getFilename(CitraApplication.getAppContext(), filepath);
|
||||||
|
}
|
||||||
|
holder.textFileName.setText(filename);
|
||||||
|
|
||||||
// TODO These shouldn't be necessary once the move to a DB-based model is complete.
|
// TODO These shouldn't be necessary once the move to a DB-based model is complete.
|
||||||
holder.gameId = mCursor.getString(GameDatabase.GAME_COLUMN_GAME_ID);
|
holder.gameId = mCursor.getString(GameDatabase.GAME_COLUMN_GAME_ID);
|
||||||
@ -99,9 +103,9 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
|||||||
holder.regions = mCursor.getString(GameDatabase.GAME_COLUMN_REGIONS);
|
holder.regions = mCursor.getString(GameDatabase.GAME_COLUMN_REGIONS);
|
||||||
holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY);
|
holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY);
|
||||||
|
|
||||||
final int backgroundColorId = isValidGame(holder.path) ? R.color.card_view_background : R.color.card_view_disabled;
|
final int backgroundColorId = isValidGame(holder.path) ? R.attr.colorSurface : R.attr.colorErrorContainer;
|
||||||
View itemView = holder.getItemView();
|
View itemView = holder.getItemView();
|
||||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), backgroundColorId));
|
itemView.setBackgroundColor(MaterialColors.getColor(itemView, backgroundColorId));
|
||||||
} else {
|
} else {
|
||||||
Log.error("[GameAdapter] Can't bind view; Cursor is not valid.");
|
Log.error("[GameAdapter] Can't bind view; Cursor is not valid.");
|
||||||
}
|
}
|
||||||
@ -204,24 +208,6 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
|||||||
EmulationActivity.launch((FragmentActivity) view.getContext(), holder.path, holder.title);
|
EmulationActivity.launch((FragmentActivity) view.getContext(), holder.path, holder.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SpacesItemDecoration extends DividerItemDecoration {
|
|
||||||
private int space;
|
|
||||||
|
|
||||||
public SpacesItemDecoration(Drawable divider, int space) {
|
|
||||||
super(divider);
|
|
||||||
this.space = space;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getItemOffsets(Rect outRect, @NonNull View view, @NonNull RecyclerView parent,
|
|
||||||
@NonNull RecyclerView.State state) {
|
|
||||||
outRect.left = 0;
|
|
||||||
outRect.right = 0;
|
|
||||||
outRect.bottom = space;
|
|
||||||
outRect.top = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isValidGame(String path) {
|
private boolean isValidGame(String path) {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
".rar", ".zip", ".7z", ".torrent", ".tar", ".gz").noneMatch(suffix -> path.toLowerCase().endsWith(suffix));
|
".rar", ".zip", ".7z", ".torrent", ".tar", ".gz").noneMatch(suffix -> path.toLowerCase().endsWith(suffix));
|
||||||
|
@ -22,6 +22,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
public final class MiiSelector {
|
public final class MiiSelector {
|
||||||
public static class MiiSelectorConfig implements java.io.Serializable {
|
public static class MiiSelectorConfig implements java.io.Serializable {
|
||||||
public boolean enable_cancel_button;
|
public boolean enable_cancel_button;
|
||||||
@ -69,8 +71,8 @@ public final class MiiSelector {
|
|||||||
? (int) config.initially_selected_mii_index
|
? (int) config.initially_selected_mii_index
|
||||||
: 0;
|
: 0;
|
||||||
data.index = initialIndex;
|
data.index = initialIndex;
|
||||||
AlertDialog.Builder builder =
|
MaterialAlertDialogBuilder builder =
|
||||||
new AlertDialog.Builder(emulationActivity)
|
new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(config.title.isEmpty()
|
.setTitle(config.title.isEmpty()
|
||||||
? emulationActivity.getString(R.string.mii_selector)
|
? emulationActivity.getString(R.string.mii_selector)
|
||||||
: config.title)
|
: config.title)
|
||||||
|
@ -19,6 +19,8 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import org.citra.citra_emu.CitraApplication;
|
import org.citra.citra_emu.CitraApplication;
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
@ -124,7 +126,7 @@ public final class SoftwareKeyboard {
|
|||||||
FrameLayout container = new FrameLayout(emulationActivity);
|
FrameLayout container = new FrameLayout(emulationActivity);
|
||||||
container.addView(editText);
|
container.addView(editText);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(R.string.software_keyboard)
|
.setTitle(R.string.software_keyboard)
|
||||||
.setView(container);
|
.setView(container);
|
||||||
setCancelable(false);
|
setCancelable(false);
|
||||||
@ -227,7 +229,7 @@ public final class SoftwareKeyboard {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
new AlertDialog.Builder(emulationActivity)
|
new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
.setTitle(R.string.software_keyboard)
|
.setTitle(R.string.software_keyboard)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package org.citra.citra_emu.contracts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class OpenFileResultContract extends ActivityResultContract<Boolean, Intent> {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Intent createIntent(@NonNull Context context, Boolean allowMultiple) {
|
||||||
|
return new Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
|
.setType("application/octet-stream")
|
||||||
|
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent parseResult(int i, @Nullable Intent intent) {
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package org.citra.citra_emu.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.citra.citra_emu.R;
|
||||||
|
import org.citra.citra_emu.utils.FileUtil;
|
||||||
|
import org.citra.citra_emu.utils.PermissionsHandler;
|
||||||
|
|
||||||
|
public class CitraDirectoryDialog extends DialogFragment {
|
||||||
|
public static final String TAG = "citra_directory_dialog_fragment";
|
||||||
|
|
||||||
|
private static final String MOVE_DATE_ENABLE = "IS_MODE_DATA_ENABLE";
|
||||||
|
|
||||||
|
TextView pathView;
|
||||||
|
|
||||||
|
TextView spaceView;
|
||||||
|
|
||||||
|
CheckBox checkBox;
|
||||||
|
|
||||||
|
AlertDialog dialog;
|
||||||
|
|
||||||
|
Listener listener;
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void onPressPositiveButton(boolean moveData, Uri path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CitraDirectoryDialog newInstance(String path, Listener listener) {
|
||||||
|
CitraDirectoryDialog frag = new CitraDirectoryDialog();
|
||||||
|
frag.listener = listener;
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString("path", path);
|
||||||
|
frag.setArguments(args);
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final FragmentActivity activity = requireActivity();
|
||||||
|
final Uri path = Uri.parse(Objects.requireNonNull(requireArguments().getString("path")));
|
||||||
|
SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
String freeSpaceText =
|
||||||
|
getResources().getString(R.string.free_space, FileUtil.getFreeSpace(activity, path));
|
||||||
|
|
||||||
|
LayoutInflater inflater = getLayoutInflater();
|
||||||
|
View view = inflater.inflate(R.layout.dialog_citra_directory, null);
|
||||||
|
|
||||||
|
checkBox = view.findViewById(R.id.checkBox);
|
||||||
|
pathView = view.findViewById(R.id.path);
|
||||||
|
spaceView = view.findViewById(R.id.space);
|
||||||
|
|
||||||
|
checkBox.setChecked(mPreferences.getBoolean(MOVE_DATE_ENABLE, true));
|
||||||
|
if (!PermissionsHandler.hasWriteAccess(activity)) {
|
||||||
|
checkBox.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
checkBox.setOnCheckedChangeListener(
|
||||||
|
(v, isChecked)
|
||||||
|
// record move data selection with SharedPreferences
|
||||||
|
-> mPreferences.edit().putBoolean(MOVE_DATE_ENABLE, checkBox.isChecked()).apply());
|
||||||
|
|
||||||
|
pathView.setText(path.getPath());
|
||||||
|
spaceView.setText(freeSpaceText);
|
||||||
|
|
||||||
|
setCancelable(false);
|
||||||
|
|
||||||
|
dialog = new MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(view)
|
||||||
|
.setIcon(R.mipmap.ic_launcher)
|
||||||
|
.setTitle(R.string.app_name)
|
||||||
|
.setPositiveButton(
|
||||||
|
android.R.string.ok,
|
||||||
|
(d, v) -> listener.onPressPositiveButton(checkBox.isChecked(), path))
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package org.citra.citra_emu.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import org.citra.citra_emu.R;
|
||||||
|
|
||||||
|
public class CopyDirProgressDialog extends DialogFragment {
|
||||||
|
public static final String TAG = "copy_dir_progress_dialog";
|
||||||
|
ProgressBar progressBar;
|
||||||
|
|
||||||
|
TextView progressText;
|
||||||
|
|
||||||
|
AlertDialog dialog;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final FragmentActivity activity = requireActivity();
|
||||||
|
|
||||||
|
LayoutInflater inflater = getLayoutInflater();
|
||||||
|
View view = inflater.inflate(R.layout.dialog_progress_bar, null);
|
||||||
|
|
||||||
|
progressBar = view.findViewById(R.id.progress_bar);
|
||||||
|
progressText = view.findViewById(R.id.progress_text);
|
||||||
|
progressText.setText("");
|
||||||
|
|
||||||
|
setCancelable(false);
|
||||||
|
|
||||||
|
dialog = new MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(view)
|
||||||
|
.setIcon(R.mipmap.ic_launcher)
|
||||||
|
.setTitle(R.string.move_data)
|
||||||
|
.setMessage("")
|
||||||
|
.create();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUpdateSearchProgress(String msg) {
|
||||||
|
requireActivity().runOnUiThread(() -> {
|
||||||
|
dialog.setMessage(getResources().getString(R.string.searching_direcotry, msg));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUpdateCopyProgress(String msg, int progress, int max) {
|
||||||
|
requireActivity().runOnUiThread(() -> {
|
||||||
|
progressBar.setProgress(progress);
|
||||||
|
progressBar.setMax(max);
|
||||||
|
progressText.setText(String.format("%d/%d", progress, max));
|
||||||
|
dialog.setMessage(getResources().getString(R.string.copy_file_name, msg));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -6,9 +6,7 @@ package org.citra.citra_emu.disk_shader_cache;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
@ -18,6 +16,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.activities.EmulationActivity;
|
import org.citra.citra_emu.activities.EmulationActivity;
|
||||||
@ -55,10 +55,10 @@ public class DiskShaderCacheProgress {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final Activity emulationActivity = Objects.requireNonNull(getActivity());
|
final Activity emulationActivity = requireActivity();
|
||||||
|
|
||||||
final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title"));
|
final String title = Objects.requireNonNull(requireArguments().getString("title"));
|
||||||
final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message"));
|
final String message = Objects.requireNonNull(requireArguments().getString("message"));
|
||||||
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(emulationActivity);
|
LayoutInflater inflater = LayoutInflater.from(emulationActivity);
|
||||||
View view = inflater.inflate(R.layout.dialog_progress_bar, null);
|
View view = inflater.inflate(R.layout.dialog_progress_bar, null);
|
||||||
@ -70,26 +70,21 @@ public class DiskShaderCacheProgress {
|
|||||||
setCancelable(false);
|
setCancelable(false);
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity);
|
|
||||||
builder.setTitle(title);
|
|
||||||
builder.setMessage(message);
|
|
||||||
builder.setView(view);
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
|
||||||
|
|
||||||
dialog = builder.create();
|
|
||||||
dialog.create();
|
|
||||||
|
|
||||||
dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener((v) -> emulationActivity.onBackPressed());
|
|
||||||
|
|
||||||
synchronized (finishLock) {
|
synchronized (finishLock) {
|
||||||
finishLock.notifyAll();
|
finishLock.notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog = new MaterialAlertDialogBuilder(emulationActivity)
|
||||||
|
.setView(view)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setNegativeButton(android.R.string.cancel, (dialog, which) -> emulationActivity.onBackPressed())
|
||||||
|
.create();
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUpdateProgress(String msg, int progress, int max) {
|
private void onUpdateProgress(String msg, int progress, int max) {
|
||||||
Objects.requireNonNull(getActivity()).runOnUiThread(() -> {
|
requireActivity().runOnUiThread(() -> {
|
||||||
progressBar.setProgress(progress);
|
progressBar.setProgress(progress);
|
||||||
progressBar.setMax(max);
|
progressBar.setMax(max);
|
||||||
progressText.setText(String.format("%d/%d", progress, max));
|
progressText.setText(String.format("%d/%d", progress, max));
|
||||||
@ -133,6 +128,8 @@ public class DiskShaderCacheProgress {
|
|||||||
case Complete:
|
case Complete:
|
||||||
// Workaround for when dialog is dismissed when the app is in the background
|
// Workaround for when dialog is dismissed when the app is in the background
|
||||||
fragment.dismissAllowingStateLoss();
|
fragment.dismissAllowingStateLoss();
|
||||||
|
|
||||||
|
emulationActivity.runOnUiThread(emulationActivity::onEmulationStarted);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,11 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.features.cheats.model.Cheat;
|
import org.citra.citra_emu.features.cheats.model.Cheat;
|
||||||
import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
|
import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
|
||||||
@ -80,12 +81,12 @@ public class CheatDetailsFragment extends Fragment {
|
|||||||
private void onDeleteClicked(View view) {
|
private void onDeleteClicked(View view) {
|
||||||
String name = mEditName.getText().toString();
|
String name = mEditName.getText().toString();
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
new MaterialAlertDialogBuilder(requireContext())
|
||||||
builder.setMessage(getString(R.string.cheats_delete_confirmation, name));
|
.setMessage(getString(R.string.cheats_delete_confirmation, name))
|
||||||
builder.setPositiveButton(android.R.string.yes,
|
.setPositiveButton(android.R.string.yes,
|
||||||
(dialog, i) -> mViewModel.deleteSelectedCheat());
|
(dialog, i) -> mViewModel.deleteSelectedCheat())
|
||||||
builder.setNegativeButton(android.R.string.no, null);
|
.setNegativeButton(android.R.string.no, null)
|
||||||
builder.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onEditClicked(View view) {
|
private void onEditClicked(View view) {
|
||||||
|
@ -7,6 +7,9 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
@ -19,6 +22,9 @@ import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
|
|||||||
import org.citra.citra_emu.ui.DividerItemDecoration;
|
import org.citra.citra_emu.ui.DividerItemDecoration;
|
||||||
|
|
||||||
public class CheatListFragment extends Fragment {
|
public class CheatListFragment extends Fragment {
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
private FloatingActionButton mFab;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
@ -28,19 +34,38 @@ public class CheatListFragment extends Fragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
RecyclerView recyclerView = view.findViewById(R.id.cheat_list);
|
mRecyclerView = view.findViewById(R.id.cheat_list);
|
||||||
FloatingActionButton fab = view.findViewById(R.id.fab);
|
mFab = view.findViewById(R.id.fab);
|
||||||
|
|
||||||
CheatsActivity activity = (CheatsActivity) requireActivity();
|
CheatsActivity activity = (CheatsActivity) requireActivity();
|
||||||
CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
|
CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
|
||||||
|
|
||||||
recyclerView.setAdapter(new CheatsAdapter(activity, viewModel));
|
mRecyclerView.setAdapter(new CheatsAdapter(activity, viewModel));
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(activity));
|
mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
|
||||||
recyclerView.addItemDecoration(new DividerItemDecoration(activity, null));
|
mRecyclerView.addItemDecoration(new DividerItemDecoration(activity, null));
|
||||||
|
|
||||||
fab.setOnClickListener(v -> {
|
mFab.setOnClickListener(v -> {
|
||||||
viewModel.startAddingCheat();
|
viewModel.startAddingCheat();
|
||||||
viewModel.openDetailsView();
|
viewModel.openDetailsView();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setInsets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInsets() {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(mRecyclerView, (v, windowInsets) -> {
|
||||||
|
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(0, 0, 0, insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_fab_list));
|
||||||
|
|
||||||
|
ViewGroup.MarginLayoutParams mlpFab =
|
||||||
|
(ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
|
||||||
|
int fabPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large);
|
||||||
|
mlpFab.leftMargin = insets.left + fabPadding;
|
||||||
|
mlpFab.bottomMargin = insets.bottom + fabPadding;
|
||||||
|
mlpFab.rightMargin = insets.right + fabPadding;
|
||||||
|
mFab.setLayoutParams(mlpFab);
|
||||||
|
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,25 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsAnimationCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.slidingpanelayout.widget.SlidingPaneLayout;
|
import androidx.slidingpanelayout.widget.SlidingPaneLayout;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
|
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.features.cheats.model.Cheat;
|
import org.citra.citra_emu.features.cheats.model.Cheat;
|
||||||
import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
|
import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
|
||||||
import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback;
|
import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback;
|
||||||
|
import org.citra.citra_emu.utils.InsetsHelper;
|
||||||
|
import org.citra.citra_emu.utils.ThemeUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class CheatsActivity extends AppCompatActivity
|
public class CheatsActivity extends AppCompatActivity
|
||||||
implements SlidingPaneLayout.PanelSlideListener {
|
implements SlidingPaneLayout.PanelSlideListener {
|
||||||
@ -37,16 +48,20 @@ public class CheatsActivity extends AppCompatActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
ThemeUtil.applyTheme(this);
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||||
|
|
||||||
mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
|
mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
|
||||||
mViewModel.load();
|
mViewModel.load();
|
||||||
|
|
||||||
setContentView(R.layout.activity_cheats);
|
setContentView(R.layout.activity_cheats);
|
||||||
|
|
||||||
mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout);
|
mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout);
|
||||||
mCheatList = findViewById(R.id.cheat_list);
|
mCheatList = findViewById(R.id.cheat_list_container);
|
||||||
mCheatDetails = findViewById(R.id.cheat_details);
|
mCheatDetails = findViewById(R.id.cheat_details_container);
|
||||||
|
|
||||||
mCheatListLastFocus = mCheatList;
|
mCheatListLastFocus = mCheatList;
|
||||||
mCheatDetailsLastFocus = mCheatDetails;
|
mCheatDetailsLastFocus = mCheatDetails;
|
||||||
@ -63,7 +78,11 @@ public class CheatsActivity extends AppCompatActivity
|
|||||||
mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView);
|
mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView);
|
||||||
|
|
||||||
// Show "Up" button in the action bar for navigation
|
// Show "Up" button in the action bar for navigation
|
||||||
|
MaterialToolbar toolbar = findViewById(R.id.toolbar_cheats);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
setInsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -146,8 +165,7 @@ public class CheatsActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setOnFocusChangeListenerRecursively(@NonNull View view,
|
public static void setOnFocusChangeListenerRecursively(@NonNull View view, View.OnFocusChangeListener listener) {
|
||||||
View.OnFocusChangeListener listener) {
|
|
||||||
view.setOnFocusChangeListener(listener);
|
view.setOnFocusChangeListener(listener);
|
||||||
|
|
||||||
if (view instanceof ViewGroup) {
|
if (view instanceof ViewGroup) {
|
||||||
@ -158,4 +176,56 @@ public class CheatsActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setInsets() {
|
||||||
|
AppBarLayout appBarLayout = findViewById(R.id.appbar_cheats);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(mSlidingPaneLayout, (v, windowInsets) -> {
|
||||||
|
Insets barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
Insets keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
|
||||||
|
|
||||||
|
InsetsHelper.insetAppBar(barInsets, appBarLayout);
|
||||||
|
mSlidingPaneLayout.setPadding(barInsets.left, 0, barInsets.right, 0);
|
||||||
|
|
||||||
|
// Set keyboard insets if the system supports smooth keyboard animations
|
||||||
|
ViewGroup.MarginLayoutParams mlpDetails =
|
||||||
|
(ViewGroup.MarginLayoutParams) mCheatDetails.getLayoutParams();
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) {
|
||||||
|
if (keyboardInsets.bottom > 0) {
|
||||||
|
mlpDetails.bottomMargin = keyboardInsets.bottom;
|
||||||
|
} else {
|
||||||
|
mlpDetails.bottomMargin = barInsets.bottom;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mlpDetails.bottomMargin == 0) {
|
||||||
|
mlpDetails.bottomMargin = barInsets.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCheatDetails.setLayoutParams(mlpDetails);
|
||||||
|
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the layout for every frame that the keyboard animates in
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
|
||||||
|
ViewCompat.setWindowInsetsAnimationCallback(mCheatDetails,
|
||||||
|
new WindowInsetsAnimationCompat.Callback(
|
||||||
|
WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP) {
|
||||||
|
int keyboardInsets = 0;
|
||||||
|
int barInsets = 0;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets,
|
||||||
|
@NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
|
||||||
|
ViewGroup.MarginLayoutParams mlpDetails =
|
||||||
|
(ViewGroup.MarginLayoutParams) mCheatDetails.getLayoutParams();
|
||||||
|
keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
|
||||||
|
barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
|
||||||
|
mlpDetails.bottomMargin = Math.max(keyboardInsets, barInsets);
|
||||||
|
mCheatDetails.setLayoutParams(mlpDetails);
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,18 +8,28 @@ import android.os.Bundle;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
|
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.utils.DirectoryInitialization;
|
import org.citra.citra_emu.utils.DirectoryInitialization;
|
||||||
import org.citra.citra_emu.utils.DirectoryStateReceiver;
|
import org.citra.citra_emu.utils.DirectoryStateReceiver;
|
||||||
import org.citra.citra_emu.utils.EmulationMenuSettings;
|
import org.citra.citra_emu.utils.EmulationMenuSettings;
|
||||||
|
import org.citra.citra_emu.utils.InsetsHelper;
|
||||||
|
import org.citra.citra_emu.utils.ThemeUtil;
|
||||||
|
|
||||||
public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView {
|
public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView {
|
||||||
private static final String ARG_MENU_TAG = "menu_tag";
|
private static final String ARG_MENU_TAG = "menu_tag";
|
||||||
@ -38,10 +48,13 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
ThemeUtil.applyTheme(this);
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_settings);
|
setContentView(R.layout.activity_settings);
|
||||||
|
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||||
|
|
||||||
Intent launcher = getIntent();
|
Intent launcher = getIntent();
|
||||||
String gameID = launcher.getStringExtra(ARG_GAME_ID);
|
String gameID = launcher.getStringExtra(ARG_GAME_ID);
|
||||||
String menuTag = launcher.getStringExtra(ARG_MENU_TAG);
|
String menuTag = launcher.getStringExtra(ARG_MENU_TAG);
|
||||||
@ -49,7 +62,11 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
|
|||||||
mPresenter.onCreate(savedInstanceState, menuTag, gameID);
|
mPresenter.onCreate(savedInstanceState, menuTag, gameID);
|
||||||
|
|
||||||
// Show "Back" button in the action bar for navigation
|
// Show "Back" button in the action bar for navigation
|
||||||
|
MaterialToolbar toolbar = findViewById(R.id.toolbar_settings);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
setInsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -107,10 +124,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
|
|||||||
if (addToStack) {
|
if (addToStack) {
|
||||||
if (areSystemAnimationsEnabled()) {
|
if (areSystemAnimationsEnabled()) {
|
||||||
transaction.setCustomAnimations(
|
transaction.setCustomAnimations(
|
||||||
R.animator.settings_enter,
|
R.anim.anim_settings_fragment_in,
|
||||||
R.animator.settings_exit,
|
R.anim.anim_settings_fragment_out,
|
||||||
R.animator.settings_pop_enter,
|
0,
|
||||||
R.animator.setttings_pop_exit);
|
R.anim.anim_pop_settings_fragment_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.addToBackStack(null);
|
transaction.addToBackStack(null);
|
||||||
@ -212,4 +229,14 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
|
|||||||
private SettingsFragment getFragment() {
|
private SettingsFragment getFragment() {
|
||||||
return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setInsets() {
|
||||||
|
AppBarLayout appBar = findViewById(R.id.appbar_settings);
|
||||||
|
FrameLayout frame = findViewById(R.id.frame_content);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(frame, (v, windowInsets) -> {
|
||||||
|
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
InsetsHelper.insetAppBar(insets, appBar);
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ package org.citra.citra_emu.features.settings.ui;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
import java.io.File;
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
import org.citra.citra_emu.features.settings.model.Settings;
|
import org.citra.citra_emu.features.settings.model.Settings;
|
||||||
import org.citra.citra_emu.features.settings.utils.SettingsFile;
|
import org.citra.citra_emu.features.settings.utils.SettingsFile;
|
||||||
@ -13,8 +15,6 @@ import org.citra.citra_emu.utils.DirectoryStateReceiver;
|
|||||||
import org.citra.citra_emu.utils.Log;
|
import org.citra.citra_emu.utils.Log;
|
||||||
import org.citra.citra_emu.utils.ThemeUtil;
|
import org.citra.citra_emu.utils.ThemeUtil;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public final class SettingsActivityPresenter {
|
public final class SettingsActivityPresenter {
|
||||||
private static final String KEY_SHOULD_SAVE = "should_save";
|
private static final String KEY_SHOULD_SAVE = "should_save";
|
||||||
|
|
||||||
@ -60,8 +60,8 @@ public final class SettingsActivityPresenter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void prepareCitraDirectoriesIfNeeded() {
|
private void prepareCitraDirectoriesIfNeeded() {
|
||||||
File configFile = new File(DirectoryInitialization.getUserDirectory() + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini");
|
DocumentFile configFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG);
|
||||||
if (!configFile.exists()) {
|
if (configFile == null || !configFile.exists()) {
|
||||||
Log.error("Citra config file could not be found!");
|
Log.error("Citra config file could not be found!");
|
||||||
}
|
}
|
||||||
if (DirectoryInitialization.areCitraDirectoriesReady()) {
|
if (DirectoryInitialization.areCitraDirectoriesReady()) {
|
||||||
@ -109,8 +109,6 @@ public final class SettingsActivityPresenter {
|
|||||||
mSettings.saveSettings(mView);
|
mSettings.saveSettings(mView);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeUtil.applyTheme();
|
|
||||||
|
|
||||||
NativeLibrary.ReloadSettings();
|
NativeLibrary.ReloadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,13 +6,16 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.DatePicker;
|
import android.widget.DatePicker;
|
||||||
import android.widget.SeekBar;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.TimePicker;
|
import android.widget.TimePicker;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.google.android.material.slider.Slider;
|
||||||
|
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.dialogs.MotionAlertDialog;
|
import org.citra.citra_emu.dialogs.MotionAlertDialog;
|
||||||
import org.citra.citra_emu.features.settings.model.FloatSetting;
|
import org.citra.citra_emu.features.settings.model.FloatSetting;
|
||||||
@ -41,15 +44,14 @@ import org.citra.citra_emu.utils.Log;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
|
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder> implements DialogInterface.OnClickListener, Slider.OnChangeListener {
|
||||||
implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener {
|
|
||||||
private SettingsFragmentView mView;
|
private SettingsFragmentView mView;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ArrayList<SettingsItem> mSettings;
|
private ArrayList<SettingsItem> mSettings;
|
||||||
|
|
||||||
private SettingsItem mClickedItem;
|
private SettingsItem mClickedItem;
|
||||||
private int mClickedPosition;
|
private int mClickedPosition;
|
||||||
private int mSeekbarProgress;
|
private int mSliderProgress;
|
||||||
|
|
||||||
private AlertDialog mDialog;
|
private AlertDialog mDialog;
|
||||||
private TextView mTextSliderValue;
|
private TextView mTextSliderValue;
|
||||||
@ -149,11 +151,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
|
|
||||||
int value = getSelectionForSingleChoiceValue(item);
|
int value = getSelectionForSingleChoiceValue(item);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity())
|
||||||
|
.setTitle(item.getNameId())
|
||||||
builder.setTitle(item.getNameId());
|
.setSingleChoiceItems(item.getChoicesId(), value, this);
|
||||||
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
|
|
||||||
|
|
||||||
mDialog = builder.show();
|
mDialog = builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,11 +162,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
|
|
||||||
int value = getSelectionForSingleChoiceValue(item);
|
int value = getSelectionForSingleChoiceValue(item);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity())
|
||||||
|
.setTitle(item.getNameId())
|
||||||
builder.setTitle(item.getNameId());
|
.setSingleChoiceItems(item.getChoicesId(), value, this);
|
||||||
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
|
|
||||||
|
|
||||||
mDialog = builder.show();
|
mDialog = builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,11 +197,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
public void onStringSingleChoiceClick(StringSingleChoiceSetting item) {
|
public void onStringSingleChoiceClick(StringSingleChoiceSetting item) {
|
||||||
mClickedItem = item;
|
mClickedItem = item;
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity())
|
||||||
|
.setTitle(item.getNameId())
|
||||||
builder.setTitle(item.getNameId());
|
.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this);
|
||||||
builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this);
|
|
||||||
|
|
||||||
mDialog = builder.show();
|
mDialog = builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,8 +222,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
mClickedItem = item;
|
mClickedItem = item;
|
||||||
mClickedPosition = position;
|
mClickedPosition = position;
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
|
|
||||||
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
|
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
|
||||||
View view = inflater.inflate(R.layout.sysclock_datetime_picker, null);
|
View view = inflater.inflate(R.layout.sysclock_datetime_picker, null);
|
||||||
|
|
||||||
@ -265,44 +259,45 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
closeDialog();
|
closeDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.setView(view);
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity())
|
||||||
builder.setPositiveButton(android.R.string.ok, ok);
|
.setView(view)
|
||||||
builder.setNegativeButton(android.R.string.cancel, defaultCancelListener);
|
.setPositiveButton(android.R.string.ok, ok)
|
||||||
|
.setNegativeButton(android.R.string.cancel, defaultCancelListener);
|
||||||
mDialog = builder.show();
|
mDialog = builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSliderClick(SliderSetting item, int position) {
|
public void onSliderClick(SliderSetting item, int position) {
|
||||||
mClickedItem = item;
|
mClickedItem = item;
|
||||||
mClickedPosition = position;
|
mClickedPosition = position;
|
||||||
mSeekbarProgress = item.getSelectedValue();
|
mSliderProgress = item.getSelectedValue();
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
|
|
||||||
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
|
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
|
||||||
View view = inflater.inflate(R.layout.dialog_seekbar, null);
|
View view = inflater.inflate(R.layout.dialog_slider, null);
|
||||||
|
|
||||||
SeekBar seekbar = view.findViewById(R.id.seekbar);
|
Slider slider = view.findViewById(R.id.slider);
|
||||||
|
|
||||||
builder.setTitle(item.getNameId());
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity())
|
||||||
builder.setView(view);
|
.setTitle(item.getNameId())
|
||||||
builder.setPositiveButton(android.R.string.ok, this);
|
.setView(view)
|
||||||
builder.setNegativeButton(android.R.string.cancel, defaultCancelListener);
|
.setPositiveButton(android.R.string.ok, this)
|
||||||
builder.setNeutralButton(R.string.slider_default, (DialogInterface dialog, int which) -> {
|
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
|
||||||
seekbar.setProgress(item.getDefaultValue());
|
.setNeutralButton(R.string.slider_default, (DialogInterface dialog, int which) -> {
|
||||||
onClick(dialog, which);
|
slider.setValue(item.getDefaultValue());
|
||||||
});
|
onClick(dialog, which);
|
||||||
|
});
|
||||||
mDialog = builder.show();
|
mDialog = builder.show();
|
||||||
|
|
||||||
mTextSliderValue = view.findViewById(R.id.text_value);
|
mTextSliderValue = view.findViewById(R.id.text_value);
|
||||||
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
|
mTextSliderValue.setText(String.valueOf(mSliderProgress));
|
||||||
|
|
||||||
TextView units = view.findViewById(R.id.text_units);
|
TextView units = view.findViewById(R.id.text_units);
|
||||||
units.setText(item.getUnits());
|
units.setText(item.getUnits());
|
||||||
|
|
||||||
seekbar.setMin(item.getMin());
|
slider.setValueFrom(item.getMin());
|
||||||
seekbar.setMax(item.getMax());
|
slider.setValueTo(item.getMax());
|
||||||
seekbar.setProgress(mSeekbarProgress);
|
slider.setValue(mSliderProgress);
|
||||||
|
|
||||||
seekbar.setOnSeekBarChangeListener(this);
|
slider.addOnChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSubmenuClick(SubmenuSetting item) {
|
public void onSubmenuClick(SubmenuSetting item) {
|
||||||
@ -375,19 +370,19 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
closeDialog();
|
closeDialog();
|
||||||
} else if (mClickedItem instanceof SliderSetting) {
|
} else if (mClickedItem instanceof SliderSetting) {
|
||||||
SliderSetting sliderSetting = (SliderSetting) mClickedItem;
|
SliderSetting sliderSetting = (SliderSetting) mClickedItem;
|
||||||
if (sliderSetting.getSelectedValue() != mSeekbarProgress) {
|
if (sliderSetting.getSelectedValue() != mSliderProgress) {
|
||||||
mView.onSettingChanged();
|
mView.onSettingChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sliderSetting.getSetting() instanceof FloatSetting) {
|
if (sliderSetting.getSetting() instanceof FloatSetting) {
|
||||||
float value = (float) mSeekbarProgress;
|
float value = (float) mSliderProgress;
|
||||||
|
|
||||||
FloatSetting setting = sliderSetting.setSelectedValue(value);
|
FloatSetting setting = sliderSetting.setSelectedValue(value);
|
||||||
if (setting != null) {
|
if (setting != null) {
|
||||||
mView.putSetting(setting);
|
mView.putSetting(setting);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress);
|
IntSetting setting = sliderSetting.setSelectedValue(mSliderProgress);
|
||||||
if (setting != null) {
|
if (setting != null) {
|
||||||
mView.putSetting(setting);
|
mView.putSetting(setting);
|
||||||
}
|
}
|
||||||
@ -397,7 +392,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
}
|
}
|
||||||
|
|
||||||
mClickedItem = null;
|
mClickedItem = null;
|
||||||
mSeekbarProgress = -1;
|
mSliderProgress = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeDialog() {
|
public void closeDialog() {
|
||||||
@ -411,20 +406,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
||||||
mSeekbarProgress = progress;
|
|
||||||
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) {
|
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) {
|
||||||
int valuesId = item.getValuesId();
|
int valuesId = item.getValuesId();
|
||||||
|
|
||||||
@ -484,4 +465,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
|
||||||
|
mSliderProgress = (int) value;
|
||||||
|
mTextSliderValue.setText(String.valueOf(mSliderProgress));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,9 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
@ -29,6 +32,8 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
|
|||||||
|
|
||||||
private SettingsAdapter mAdapter;
|
private SettingsAdapter mAdapter;
|
||||||
|
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
|
||||||
public static Fragment newInstance(String menuTag, String gameId) {
|
public static Fragment newInstance(String menuTag, String gameId) {
|
||||||
SettingsFragment fragment = new SettingsFragment();
|
SettingsFragment fragment = new SettingsFragment();
|
||||||
|
|
||||||
@ -71,15 +76,17 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
|
|||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
LinearLayoutManager manager = new LinearLayoutManager(getActivity());
|
LinearLayoutManager manager = new LinearLayoutManager(getActivity());
|
||||||
|
|
||||||
RecyclerView recyclerView = view.findViewById(R.id.list_settings);
|
mRecyclerView = view.findViewById(R.id.list_settings);
|
||||||
|
|
||||||
recyclerView.setAdapter(mAdapter);
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
recyclerView.setLayoutManager(manager);
|
mRecyclerView.setLayoutManager(manager);
|
||||||
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
|
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
|
||||||
|
|
||||||
SettingsActivityView activity = (SettingsActivityView) getActivity();
|
SettingsActivityView activity = (SettingsActivityView) getActivity();
|
||||||
|
|
||||||
mPresenter.onViewCreated(activity.getSettings());
|
mPresenter.onViewCreated(activity.getSettings());
|
||||||
|
|
||||||
|
setInsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -133,4 +140,12 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
|
|||||||
public void onSettingChanged() {
|
public void onSettingChanged() {
|
||||||
mActivity.onSettingChanged();
|
mActivity.onSettingChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setInsets() {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(mRecyclerView, (v, windowInsets) -> {
|
||||||
|
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(insets.left, 0, insets.right, insets.bottom);
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +355,9 @@ public final class SettingsFragmentPresenter {
|
|||||||
mView.getActivity().setTitle(R.string.preferences_graphics);
|
mView.getActivity().setTitle(R.string.preferences_graphics);
|
||||||
|
|
||||||
SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
|
SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
|
||||||
|
Setting graphicsApi = rendererSection.getSetting(SettingsFile.KEY_GRAPHICS_API);
|
||||||
|
Setting spvShaderGen = rendererSection.getSetting(SettingsFile.KEY_SPIRV_SHADER_GEN);
|
||||||
|
Setting asyncShaders = rendererSection.getSetting(SettingsFile.KEY_ASYNC_SHADERS);
|
||||||
Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR);
|
Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR);
|
||||||
Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE);
|
Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE);
|
||||||
Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL);
|
Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL);
|
||||||
@ -371,6 +374,9 @@ public final class SettingsFragmentPresenter {
|
|||||||
//Setting preloadTextures = utilitySection.getSetting(SettingsFile.KEY_PRELOAD_TEXTURES);
|
//Setting preloadTextures = utilitySection.getSetting(SettingsFile.KEY_PRELOAD_TEXTURES);
|
||||||
|
|
||||||
sl.add(new HeaderSetting(null, null, R.string.renderer, 0));
|
sl.add(new HeaderSetting(null, null, R.string.renderer, 0));
|
||||||
|
sl.add(new SingleChoiceSetting(SettingsFile.KEY_GRAPHICS_API, Settings.SECTION_RENDERER, R.string.graphics_api, 0, R.array.graphicsApiNames, R.array.graphicsApiValues, 2, graphicsApi));
|
||||||
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_SPIRV_SHADER_GEN, Settings.SECTION_RENDERER, R.string.spirv_shader_gen, R.string.spirv_shader_gen_description, true, spvShaderGen));
|
||||||
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_ASYNC_SHADERS, Settings.SECTION_RENDERER, R.string.async_shaders, R.string.async_shaders_description, false, asyncShaders));
|
||||||
sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor));
|
sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor));
|
||||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode));
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode));
|
||||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul));
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul));
|
||||||
@ -412,11 +418,13 @@ public final class SettingsFragmentPresenter {
|
|||||||
Setting hardwareRenderer = rendererSection.getSetting(SettingsFile.KEY_HW_RENDERER);
|
Setting hardwareRenderer = rendererSection.getSetting(SettingsFile.KEY_HW_RENDERER);
|
||||||
Setting hardwareShader = rendererSection.getSetting(SettingsFile.KEY_HW_SHADER);
|
Setting hardwareShader = rendererSection.getSetting(SettingsFile.KEY_HW_SHADER);
|
||||||
Setting vsyncEnable = rendererSection.getSetting(SettingsFile.KEY_USE_VSYNC);
|
Setting vsyncEnable = rendererSection.getSetting(SettingsFile.KEY_USE_VSYNC);
|
||||||
|
Setting rendererDebug = rendererSection.getSetting(SettingsFile.KEY_RENDERER_DEBUG);
|
||||||
|
|
||||||
sl.add(new HeaderSetting(null, null, R.string.debug_warning, 0));
|
sl.add(new HeaderSetting(null, null, R.string.debug_warning, 0));
|
||||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView));
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView));
|
||||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, Settings.SECTION_RENDERER, R.string.hw_renderer, R.string.hw_renderer_description, true, hardwareRenderer, true, mView));
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, Settings.SECTION_RENDERER, R.string.hw_renderer, R.string.hw_renderer_description, true, hardwareRenderer, true, mView));
|
||||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView));
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView));
|
||||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable));
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable));
|
||||||
|
sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_DEBUG, Settings.SECTION_RENDERER, R.string.renderer_debug, R.string.renderer_debug_description, false, rendererDebug));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package org.citra.citra_emu.features.settings.ui;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FrameLayout subclass with few Properties added to simplify animations.
|
|
||||||
* Don't remove the methods appearing as unused, in order not to break the menu animations
|
|
||||||
*/
|
|
||||||
public final class SettingsFrameLayout extends FrameLayout {
|
|
||||||
private float mVisibleness = 1.0f;
|
|
||||||
|
|
||||||
public SettingsFrameLayout(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettingsFrameLayout(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getYFraction() {
|
|
||||||
return getY() / getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setYFraction(float yFraction) {
|
|
||||||
final int height = getHeight();
|
|
||||||
setY((height > 0) ? (yFraction * height) : -9999);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getVisibleness() {
|
|
||||||
return mVisibleness;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVisibleness(float visibleness) {
|
|
||||||
setScaleX(visibleness);
|
|
||||||
setScaleY(visibleness);
|
|
||||||
setAlpha(visibleness);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,10 @@
|
|||||||
package org.citra.citra_emu.features.settings.utils;
|
package org.citra.citra_emu.features.settings.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
import org.citra.citra_emu.CitraApplication;
|
import org.citra.citra_emu.CitraApplication;
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
@ -18,10 +22,11 @@ import org.citra.citra_emu.utils.Log;
|
|||||||
import org.ini4j.Wini;
|
import org.ini4j.Wini;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@ -39,6 +44,10 @@ public final class SettingsFile {
|
|||||||
|
|
||||||
public static final String KEY_PREMIUM = "premium";
|
public static final String KEY_PREMIUM = "premium";
|
||||||
|
|
||||||
|
public static final String KEY_GRAPHICS_API = "graphics_api";
|
||||||
|
public static final String KEY_SPIRV_SHADER_GEN = "spirv_shader_gen";
|
||||||
|
public static final String KEY_RENDERER_DEBUG = "renderer_debug";
|
||||||
|
public static final String KEY_ASYNC_SHADERS = "async_shader_compilation";
|
||||||
public static final String KEY_HW_RENDERER = "use_hw_renderer";
|
public static final String KEY_HW_RENDERER = "use_hw_renderer";
|
||||||
public static final String KEY_HW_SHADER = "use_hw_shader";
|
public static final String KEY_HW_SHADER = "use_hw_shader";
|
||||||
public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul";
|
public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul";
|
||||||
@ -145,13 +154,15 @@ public final class SettingsFile {
|
|||||||
* @param view The current view.
|
* @param view The current view.
|
||||||
* @return An Observable that emits a HashMap of the file's contents, then completes.
|
* @return An Observable that emits a HashMap of the file's contents, then completes.
|
||||||
*/
|
*/
|
||||||
static HashMap<String, SettingSection> readFile(final File ini, boolean isCustomGame, SettingsActivityView view) {
|
static HashMap<String, SettingSection> readFile(final DocumentFile ini, boolean isCustomGame, SettingsActivityView view) {
|
||||||
HashMap<String, SettingSection> sections = new Settings.SettingsSectionMap();
|
HashMap<String, SettingSection> sections = new Settings.SettingsSectionMap();
|
||||||
|
|
||||||
BufferedReader reader = null;
|
BufferedReader reader = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reader = new BufferedReader(new FileReader(ini));
|
Context context = CitraApplication.getAppContext();
|
||||||
|
InputStream inputStream = context.getContentResolver().openInputStream(ini.getUri());
|
||||||
|
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
|
||||||
SettingSection current = null;
|
SettingSection current = null;
|
||||||
for (String line; (line = reader.readLine()) != null; ) {
|
for (String line; (line = reader.readLine()) != null; ) {
|
||||||
@ -166,11 +177,11 @@ public final class SettingsFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.error("[SettingsFile] File not found: " + ini.getAbsolutePath() + e.getMessage());
|
Log.error("[SettingsFile] File not found: " + ini.getUri() + e.getMessage());
|
||||||
if (view != null)
|
if (view != null)
|
||||||
view.onSettingsFileNotFound();
|
view.onSettingsFileNotFound();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.error("[SettingsFile] Error reading from: " + ini.getAbsolutePath() + e.getMessage());
|
Log.error("[SettingsFile] Error reading from: " + ini.getUri() + e.getMessage());
|
||||||
if (view != null)
|
if (view != null)
|
||||||
view.onSettingsFileNotFound();
|
view.onSettingsFileNotFound();
|
||||||
} finally {
|
} finally {
|
||||||
@ -178,7 +189,7 @@ public final class SettingsFile {
|
|||||||
try {
|
try {
|
||||||
reader.close();
|
reader.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.error("[SettingsFile] Error closing: " + ini.getAbsolutePath() + e.getMessage());
|
Log.error("[SettingsFile] Error closing: " + ini.getUri() + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,17 +223,23 @@ public final class SettingsFile {
|
|||||||
*/
|
*/
|
||||||
public static void saveFile(final String fileName, TreeMap<String, SettingSection> sections,
|
public static void saveFile(final String fileName, TreeMap<String, SettingSection> sections,
|
||||||
SettingsActivityView view) {
|
SettingsActivityView view) {
|
||||||
File ini = getSettingsFile(fileName);
|
DocumentFile ini = getSettingsFile(fileName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Wini writer = new Wini(ini);
|
Context context = CitraApplication.getAppContext();
|
||||||
|
InputStream inputStream = context.getContentResolver().openInputStream(ini.getUri());
|
||||||
|
Wini writer = new Wini(inputStream);
|
||||||
|
|
||||||
Set<String> keySet = sections.keySet();
|
Set<String> keySet = sections.keySet();
|
||||||
for (String key : keySet) {
|
for (String key : keySet) {
|
||||||
SettingSection section = sections.get(key);
|
SettingSection section = sections.get(key);
|
||||||
writeSection(writer, section);
|
writeSection(writer, section);
|
||||||
}
|
}
|
||||||
writer.store();
|
inputStream.close();
|
||||||
|
OutputStream outputStream = context.getContentResolver().openOutputStream(ini.getUri());
|
||||||
|
writer.store(outputStream);
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage());
|
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage());
|
||||||
view.showToastMessage(CitraApplication.getAppContext().getString(R.string.error_saving, fileName, e.getMessage()), false);
|
view.showToastMessage(CitraApplication.getAppContext().getString(R.string.error_saving, fileName, e.getMessage()), false);
|
||||||
@ -262,14 +279,16 @@ public final class SettingsFile {
|
|||||||
return generalSectionName;
|
return generalSectionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
public static DocumentFile getSettingsFile(String fileName) {
|
||||||
private static File getSettingsFile(String fileName) {
|
DocumentFile root = DocumentFile.fromTreeUri(CitraApplication.getAppContext(), Uri.parse(DirectoryInitialization.getUserDirectory()));
|
||||||
return new File(
|
DocumentFile configDirectory = root.findFile("config");
|
||||||
DirectoryInitialization.getUserDirectory() + "/config/" + fileName + ".ini");
|
return configDirectory.findFile(fileName + ".ini");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getCustomGameSettingsFile(String gameId) {
|
private static DocumentFile getCustomGameSettingsFile(String gameId) {
|
||||||
return new File(DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini");
|
DocumentFile root = DocumentFile.fromTreeUri(CitraApplication.getAppContext(), Uri.parse(DirectoryInitialization.getUserDirectory()));
|
||||||
|
DocumentFile configDirectory = root.findFile("GameSettings");
|
||||||
|
return configDirectory.findFile(gameId + ".ini");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SettingSection sectionFromLine(String line, boolean isCustomGame) {
|
private static SettingSection sectionFromLine(String line, boolean isCustomGame) {
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
package org.citra.citra_emu.fragments;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerFragment;
|
|
||||||
|
|
||||||
import org.citra.citra_emu.R;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CustomFilePickerFragment extends FilePickerFragment {
|
|
||||||
private static String ALL_FILES = "*";
|
|
||||||
private int mTitle;
|
|
||||||
private static List<String> extensions = Collections.singletonList(ALL_FILES);
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Uri toUri(@NonNull final File file) {
|
|
||||||
return FileProvider
|
|
||||||
.getUriForFile(getContext(),
|
|
||||||
getContext().getApplicationContext().getPackageName() + ".filesprovider",
|
|
||||||
file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
if (mode == MODE_DIR) {
|
|
||||||
TextView ok = getActivity().findViewById(R.id.nnf_button_ok);
|
|
||||||
ok.setText(R.string.select_dir);
|
|
||||||
|
|
||||||
TextView cancel = getActivity().findViewById(R.id.nnf_button_cancel);
|
|
||||||
cancel.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected View inflateRootView(LayoutInflater inflater, ViewGroup container) {
|
|
||||||
View view = super.inflateRootView(inflater, container);
|
|
||||||
if (mTitle != 0) {
|
|
||||||
Toolbar toolbar = view.findViewById(com.nononsenseapps.filepicker.R.id.nnf_picker_toolbar);
|
|
||||||
ViewGroup parent = (ViewGroup) toolbar.getParent();
|
|
||||||
int index = parent.indexOfChild(toolbar);
|
|
||||||
View newToolbar = inflater.inflate(R.layout.filepicker_toolbar, toolbar, false);
|
|
||||||
TextView title = newToolbar.findViewById(R.id.filepicker_title);
|
|
||||||
title.setText(mTitle);
|
|
||||||
parent.removeView(toolbar);
|
|
||||||
parent.addView(newToolbar, index);
|
|
||||||
}
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(int title) {
|
|
||||||
mTitle = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAllowedExtensions(String allowedExtensions) {
|
|
||||||
if (allowedExtensions == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
extensions = Arrays.asList(allowedExtensions.split(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isItemVisible(@NonNull final File file) {
|
|
||||||
// Some users jump to the conclusion that Dolphin isn't able to detect their
|
|
||||||
// files if the files don't show up in the file picker when mode == MODE_DIR.
|
|
||||||
// To avoid this, show files even when the user needs to select a directory.
|
|
||||||
return (showHiddenItems || !file.isHidden()) &&
|
|
||||||
(file.isDirectory() || extensions.contains(ALL_FILES) ||
|
|
||||||
extensions.contains(fileExtension(file.getName()).toLowerCase()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCheckable(@NonNull final File file) {
|
|
||||||
// We need to make a small correction to the isCheckable logic due to
|
|
||||||
// overriding isItemVisible to show files when mode == MODE_DIR.
|
|
||||||
// AbstractFilePickerFragment always treats files as checkable when
|
|
||||||
// allowExistingFile == true, but we don't want files to be checkable when mode == MODE_DIR.
|
|
||||||
return super.isCheckable(file) && !(mode == MODE_DIR && file.isFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void goUp() {
|
|
||||||
if (Environment.getExternalStorageDirectory().getPath().equals(mCurrentPath.getPath())) {
|
|
||||||
goToDir(new File("/storage/"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mCurrentPath.equals(new File("/storage/"))){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClickDir(@NonNull View view, @NonNull DirViewHolder viewHolder) {
|
|
||||||
if(viewHolder.file.equals(new File("/storage/emulated/")))
|
|
||||||
viewHolder.file = new File("/storage/emulated/0/");
|
|
||||||
super.onClickDir(view, viewHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fileExtension(@NonNull String filename) {
|
|
||||||
int i = filename.lastIndexOf('.');
|
|
||||||
return i < 0 ? "" : filename.substring(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
package org.citra.citra_emu.model;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A struct that is much more "cheaper" than DocumentFile.
|
||||||
|
* Only contains the information we needed.
|
||||||
|
*/
|
||||||
|
public class CheapDocument {
|
||||||
|
private final String filename;
|
||||||
|
private final Uri uri;
|
||||||
|
private final String mimeType;
|
||||||
|
|
||||||
|
public CheapDocument(String filename, String mimeType, Uri uri) {
|
||||||
|
this.filename = filename;
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMimeType() {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR);
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,10 @@ import android.content.Context;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
|
import org.citra.citra_emu.utils.FileUtil;
|
||||||
import org.citra.citra_emu.utils.Log;
|
import org.citra.citra_emu.utils.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -64,10 +66,12 @@ public final class GameDatabase extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
private static final String SQL_DELETE_FOLDERS = "DROP TABLE IF EXISTS " + TABLE_NAME_FOLDERS;
|
private static final String SQL_DELETE_FOLDERS = "DROP TABLE IF EXISTS " + TABLE_NAME_FOLDERS;
|
||||||
private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES;
|
private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES;
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
public GameDatabase(Context context) {
|
public GameDatabase(Context context) {
|
||||||
// Superclass constructor builds a database or uses an existing one.
|
// Superclass constructor builds a database or uses an existing one.
|
||||||
super(context, "games.db", null, DB_VERSION);
|
super(context, "games.db", null, DB_VERSION);
|
||||||
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -151,9 +155,10 @@ public final class GameDatabase extends SQLiteOpenHelper {
|
|||||||
while (folderCursor.moveToNext()) {
|
while (folderCursor.moveToNext()) {
|
||||||
String folderPath = folderCursor.getString(FOLDER_COLUMN_PATH);
|
String folderPath = folderCursor.getString(FOLDER_COLUMN_PATH);
|
||||||
|
|
||||||
File folder = new File(folderPath);
|
Uri folder = Uri.parse(folderPath);
|
||||||
// If the folder is empty because it no longer exists, remove it from the library.
|
// If the folder is empty because it no longer exists, remove it from the library.
|
||||||
if (!folder.exists()) {
|
CheapDocument[] files = FileUtil.listFiles(mContext, folder);
|
||||||
|
if (files.length == 0) {
|
||||||
Log.error(
|
Log.error(
|
||||||
"[GameDatabase] Folder no longer exists. Removing from the library: " + folderPath);
|
"[GameDatabase] Folder no longer exists. Removing from the library: " + folderPath);
|
||||||
database.delete(TABLE_NAME_FOLDERS,
|
database.delete(TABLE_NAME_FOLDERS,
|
||||||
@ -161,7 +166,7 @@ public final class GameDatabase extends SQLiteOpenHelper {
|
|||||||
new String[]{Long.toString(folderCursor.getLong(COLUMN_DB_ID))});
|
new String[]{Long.toString(folderCursor.getLong(COLUMN_DB_ID))});
|
||||||
}
|
}
|
||||||
|
|
||||||
addGamesRecursive(database, folder, allowedExtensions, 3);
|
addGamesRecursive(database, files, allowedExtensions, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
fileCursor.close();
|
fileCursor.close();
|
||||||
@ -173,33 +178,28 @@ public final class GameDatabase extends SQLiteOpenHelper {
|
|||||||
database.close();
|
database.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addGamesRecursive(SQLiteDatabase database, File parent, Set<String> allowedExtensions, int depth) {
|
private void addGamesRecursive(SQLiteDatabase database, CheapDocument[] files,
|
||||||
|
Set<String> allowedExtensions, int depth) {
|
||||||
if (depth <= 0) {
|
if (depth <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File[] children = parent.listFiles();
|
for (CheapDocument file : files) {
|
||||||
if (children != null) {
|
if (file.isDirectory()) {
|
||||||
for (File file : children) {
|
Set<String> newExtensions = new HashSet<>(Arrays.asList(
|
||||||
if (file.isHidden()) {
|
".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app"));
|
||||||
continue;
|
CheapDocument[] children = FileUtil.listFiles(mContext, file.getUri());
|
||||||
}
|
this.addGamesRecursive(database, children, newExtensions, depth - 1);
|
||||||
|
} else {
|
||||||
|
String filename = file.getUri().toString();
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
int extensionStart = filename.lastIndexOf('.');
|
||||||
Set<String> newExtensions = new HashSet<>(Arrays.asList(
|
if (extensionStart > 0) {
|
||||||
".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app"));
|
String fileExtension = filename.substring(extensionStart);
|
||||||
addGamesRecursive(database, file, newExtensions, depth - 1);
|
|
||||||
} else {
|
|
||||||
String filePath = file.getPath();
|
|
||||||
|
|
||||||
int extensionStart = filePath.lastIndexOf('.');
|
// Check that the file has an extension we care about before trying to read out of it.
|
||||||
if (extensionStart > 0) {
|
if (allowedExtensions.contains(fileExtension.toLowerCase())) {
|
||||||
String fileExtension = filePath.substring(extensionStart);
|
attemptToAddGame(database, filename);
|
||||||
|
|
||||||
// Check that the file has an extension we care about before trying to read out of it.
|
|
||||||
if (allowedExtensions.contains(fileExtension.toLowerCase())) {
|
|
||||||
attemptToAddGame(database, filePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package org.citra.citra_emu.ui;
|
package org.citra.citra_emu.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -32,6 +34,13 @@ public class TwoPaneOnBackPressedCallback extends OnBackPressedCallback
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPanelClosed(@NonNull View panel) {
|
public void onPanelClosed(@NonNull View panel) {
|
||||||
|
closeKeyboard();
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void closeKeyboard() {
|
||||||
|
InputMethodManager manager = (InputMethodManager) mSlidingPaneLayout.getContext()
|
||||||
|
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
manager.hideSoftInputFromWindow(mSlidingPaneLayout.getRootView().getWindowToken(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,46 @@
|
|||||||
package org.citra.citra_emu.ui.main;
|
package org.citra.citra_emu.ui.main;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.splashscreen.SplashScreen;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import java.util.Collections;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.activities.EmulationActivity;
|
import org.citra.citra_emu.activities.EmulationActivity;
|
||||||
|
import org.citra.citra_emu.contracts.OpenFileResultContract;
|
||||||
import org.citra.citra_emu.features.settings.ui.SettingsActivity;
|
import org.citra.citra_emu.features.settings.ui.SettingsActivity;
|
||||||
import org.citra.citra_emu.model.GameProvider;
|
import org.citra.citra_emu.model.GameProvider;
|
||||||
import org.citra.citra_emu.ui.platform.PlatformGamesFragment;
|
import org.citra.citra_emu.ui.platform.PlatformGamesFragment;
|
||||||
import org.citra.citra_emu.utils.AddDirectoryHelper;
|
import org.citra.citra_emu.utils.AddDirectoryHelper;
|
||||||
import org.citra.citra_emu.utils.BillingManager;
|
import org.citra.citra_emu.utils.BillingManager;
|
||||||
|
import org.citra.citra_emu.utils.CitraDirectoryHelper;
|
||||||
import org.citra.citra_emu.utils.DirectoryInitialization;
|
import org.citra.citra_emu.utils.DirectoryInitialization;
|
||||||
import org.citra.citra_emu.utils.FileBrowserHelper;
|
import org.citra.citra_emu.utils.FileBrowserHelper;
|
||||||
|
import org.citra.citra_emu.utils.InsetsHelper;
|
||||||
import org.citra.citra_emu.utils.PermissionsHandler;
|
import org.citra.citra_emu.utils.PermissionsHandler;
|
||||||
import org.citra.citra_emu.utils.PicassoUtils;
|
import org.citra.citra_emu.utils.PicassoUtils;
|
||||||
import org.citra.citra_emu.utils.StartupHandler;
|
import org.citra.citra_emu.utils.StartupHandler;
|
||||||
import org.citra.citra_emu.utils.ThemeUtil;
|
import org.citra.citra_emu.utils.ThemeUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which
|
* The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which
|
||||||
* individually display a grid of available games for each Fragment, in a tabbed layout.
|
* individually display a grid of available games for each Fragment, in a tabbed layout.
|
||||||
@ -46,13 +57,72 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
|
|
||||||
private static MenuItem mPremiumButton;
|
private static MenuItem mPremiumButton;
|
||||||
|
|
||||||
|
private final CitraDirectoryHelper citraDirectoryHelper = new CitraDirectoryHelper(this, () -> {
|
||||||
|
// If mPlatformGamesFragment is null means game directory have not been set yet.
|
||||||
|
if (mPlatformGamesFragment == null) {
|
||||||
|
mPlatformGamesFragment = new PlatformGamesFragment();
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.add(mFrameLayoutId, mPlatformGamesFragment)
|
||||||
|
.commit();
|
||||||
|
showGameInstallDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<Uri> mOpenCitraDirectory =
|
||||||
|
registerForActivityResult(new ActivityResultContracts.OpenDocumentTree(), result -> {
|
||||||
|
if (result == null)
|
||||||
|
return;
|
||||||
|
citraDirectoryHelper.showCitraDirectoryDialog(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<Uri> mOpenGameListLauncher =
|
||||||
|
registerForActivityResult(new ActivityResultContracts.OpenDocumentTree(), result -> {
|
||||||
|
if (result == null)
|
||||||
|
return;
|
||||||
|
int takeFlags =
|
||||||
|
(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
getContentResolver().takePersistableUriPermission(result, takeFlags);
|
||||||
|
// When a new directory is picked, we currently will reset the existing games
|
||||||
|
// database. This effectively means that only one game directory is supported.
|
||||||
|
// TODO(bunnei): Consider fixing this in the future, or removing code for this.
|
||||||
|
getContentResolver().insert(GameProvider.URI_RESET, null);
|
||||||
|
// Add the new directory
|
||||||
|
mPresenter.onDirectorySelected(result.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<Boolean> mOpenFileLauncher =
|
||||||
|
registerForActivityResult(new OpenFileResultContract(), result -> {
|
||||||
|
if (result == null)
|
||||||
|
return;
|
||||||
|
String[] selectedFiles = FileBrowserHelper.getSelectedFiles(
|
||||||
|
result, getApplicationContext(), Collections.singletonList("cia"));
|
||||||
|
if (selectedFiles == null) {
|
||||||
|
Toast
|
||||||
|
.makeText(getApplicationContext(), R.string.cia_file_not_found,
|
||||||
|
Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NativeLibrary.InstallCIAS(selectedFiles);
|
||||||
|
mPresenter.refreshGameList();
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
ThemeUtil.applyTheme();
|
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||||
|
splashScreen.setKeepOnScreenCondition(
|
||||||
|
()
|
||||||
|
-> (PermissionsHandler.hasWriteAccess(this) &&
|
||||||
|
!DirectoryInitialization.areCitraDirectoriesReady()));
|
||||||
|
|
||||||
|
ThemeUtil.applyTheme(this);
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||||
|
|
||||||
findViews();
|
findViews();
|
||||||
|
|
||||||
setSupportActionBar(mToolbar);
|
setSupportActionBar(mToolbar);
|
||||||
@ -61,7 +131,7 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
mPresenter.onCreate();
|
mPresenter.onCreate();
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
StartupHandler.HandleInit(this);
|
StartupHandler.HandleInit(this, mOpenCitraDirectory);
|
||||||
if (PermissionsHandler.hasWriteAccess(this)) {
|
if (PermissionsHandler.hasWriteAccess(this)) {
|
||||||
mPlatformGamesFragment = new PlatformGamesFragment();
|
mPlatformGamesFragment = new PlatformGamesFragment();
|
||||||
getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment)
|
getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment)
|
||||||
@ -77,6 +147,8 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
|
|
||||||
// Dismiss previous notifications (should not happen unless a crash occurred)
|
// Dismiss previous notifications (should not happen unless a crash occurred)
|
||||||
EmulationActivity.tryDismissRunningNotification(this);
|
EmulationActivity.tryDismissRunningNotification(this);
|
||||||
|
|
||||||
|
setInsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -144,7 +216,7 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
if (PermissionsHandler.hasWriteAccess(this)) {
|
if (PermissionsHandler.hasWriteAccess(this)) {
|
||||||
SettingsActivity.launch(this, menuTag, "");
|
SettingsActivity.launch(this, menuTag, "");
|
||||||
} else {
|
} else {
|
||||||
PermissionsHandler.checkWritePermission(this);
|
PermissionsHandler.checkWritePermission(this, mOpenCitraDirectory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,79 +224,18 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
public void launchFileListActivity(int request) {
|
public void launchFileListActivity(int request) {
|
||||||
if (PermissionsHandler.hasWriteAccess(this)) {
|
if (PermissionsHandler.hasWriteAccess(this)) {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
|
case MainPresenter.REQUEST_SELECT_CITRA_DIRECTORY:
|
||||||
|
mOpenCitraDirectory.launch(null);
|
||||||
|
break;
|
||||||
case MainPresenter.REQUEST_ADD_DIRECTORY:
|
case MainPresenter.REQUEST_ADD_DIRECTORY:
|
||||||
FileBrowserHelper.openDirectoryPicker(this,
|
mOpenGameListLauncher.launch(null);
|
||||||
MainPresenter.REQUEST_ADD_DIRECTORY,
|
break;
|
||||||
R.string.select_game_folder,
|
|
||||||
Arrays.asList("elf", "axf", "cci", "3ds",
|
|
||||||
"cxi", "app", "3dsx", "cia",
|
|
||||||
"rar", "zip", "7z", "torrent",
|
|
||||||
"tar", "gz"));
|
|
||||||
break;
|
|
||||||
case MainPresenter.REQUEST_INSTALL_CIA:
|
case MainPresenter.REQUEST_INSTALL_CIA:
|
||||||
FileBrowserHelper.openFilePicker(this, MainPresenter.REQUEST_INSTALL_CIA,
|
mOpenFileLauncher.launch(true);
|
||||||
R.string.install_cia_title,
|
break;
|
||||||
Collections.singletonList("cia"), true);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PermissionsHandler.checkWritePermission(this);
|
PermissionsHandler.checkWritePermission(this, mOpenCitraDirectory);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
|
||||||
* @param resultCode An int describing what Activity is giving us this callback.
|
|
||||||
* @param result The information the returning Activity is providing us.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent result) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, result);
|
|
||||||
switch (requestCode) {
|
|
||||||
case MainPresenter.REQUEST_ADD_DIRECTORY:
|
|
||||||
// If the user picked a file, as opposed to just backing out.
|
|
||||||
if (resultCode == MainActivity.RESULT_OK) {
|
|
||||||
// When a new directory is picked, we currently will reset the existing games
|
|
||||||
// database. This effectively means that only one game directory is supported.
|
|
||||||
// TODO(bunnei): Consider fixing this in the future, or removing code for this.
|
|
||||||
getContentResolver().insert(GameProvider.URI_RESET, null);
|
|
||||||
// Add the new directory
|
|
||||||
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MainPresenter.REQUEST_INSTALL_CIA:
|
|
||||||
// If the user picked a file, as opposed to just backing out.
|
|
||||||
if (resultCode == MainActivity.RESULT_OK) {
|
|
||||||
NativeLibrary.InstallCIAS(FileBrowserHelper.getSelectedFiles(result));
|
|
||||||
mPresenter.refeshGameList();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
||||||
switch (requestCode) {
|
|
||||||
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
|
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
DirectoryInitialization.start(this);
|
|
||||||
|
|
||||||
mPlatformGamesFragment = new PlatformGamesFragment();
|
|
||||||
getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment)
|
|
||||||
.commit();
|
|
||||||
|
|
||||||
// Immediately prompt user to select a game directory on first boot
|
|
||||||
if (mPresenter != null) {
|
|
||||||
mPresenter.launchFileListActivity(MainPresenter.REQUEST_ADD_DIRECTORY);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +256,18 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showGameInstallDialog() {
|
||||||
|
new MaterialAlertDialogBuilder(this)
|
||||||
|
.setIcon(R.mipmap.ic_launcher)
|
||||||
|
.setTitle(R.string.app_name)
|
||||||
|
.setMessage(R.string.app_game_install_description)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(android.R.string.ok,
|
||||||
|
(d, v) -> mOpenGameListLauncher.launch(null))
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
EmulationActivity.tryDismissRunningNotification(this);
|
EmulationActivity.tryDismissRunningNotification(this);
|
||||||
@ -266,4 +289,15 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
|||||||
public static void invokePremiumBilling(Runnable callback) {
|
public static void invokePremiumBilling(Runnable callback) {
|
||||||
mBillingManager.invokePremiumBilling(callback);
|
mBillingManager.invokePremiumBilling(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setInsets() {
|
||||||
|
AppBarLayout appBar = findViewById(R.id.appbar);
|
||||||
|
FrameLayout frame = findViewById(R.id.games_platform_frame);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(frame, (v, windowInsets) -> {
|
||||||
|
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
InsetsHelper.insetAppBar(insets, appBar);
|
||||||
|
frame.setPadding(insets.left, 0, insets.right, 0);
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.citra.citra_emu.ui.main;
|
package org.citra.citra_emu.ui.main;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import org.citra.citra_emu.BuildConfig;
|
import org.citra.citra_emu.BuildConfig;
|
||||||
@ -9,10 +10,12 @@ import org.citra.citra_emu.features.settings.model.Settings;
|
|||||||
import org.citra.citra_emu.features.settings.utils.SettingsFile;
|
import org.citra.citra_emu.features.settings.utils.SettingsFile;
|
||||||
import org.citra.citra_emu.model.GameDatabase;
|
import org.citra.citra_emu.model.GameDatabase;
|
||||||
import org.citra.citra_emu.utils.AddDirectoryHelper;
|
import org.citra.citra_emu.utils.AddDirectoryHelper;
|
||||||
|
import org.citra.citra_emu.utils.PermissionsHandler;
|
||||||
|
|
||||||
public final class MainPresenter {
|
public final class MainPresenter {
|
||||||
public static final int REQUEST_ADD_DIRECTORY = 1;
|
public static final int REQUEST_ADD_DIRECTORY = 1;
|
||||||
public static final int REQUEST_INSTALL_CIA = 2;
|
public static final int REQUEST_INSTALL_CIA = 2;
|
||||||
|
public static final int REQUEST_SELECT_CITRA_DIRECTORY = 3;
|
||||||
|
|
||||||
private final MainView mView;
|
private final MainView mView;
|
||||||
private String mDirToAdd;
|
private String mDirToAdd;
|
||||||
@ -25,7 +28,7 @@ public final class MainPresenter {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
String versionName = BuildConfig.VERSION_NAME;
|
String versionName = BuildConfig.VERSION_NAME;
|
||||||
mView.setVersionString(versionName);
|
mView.setVersionString(versionName);
|
||||||
refeshGameList();
|
refreshGameList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void launchFileListActivity(int request) {
|
public void launchFileListActivity(int request) {
|
||||||
@ -46,6 +49,10 @@ public final class MainPresenter {
|
|||||||
mView.launchSettingsActivity(SettingsFile.FILE_NAME_CONFIG);
|
mView.launchSettingsActivity(SettingsFile.FILE_NAME_CONFIG);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case R.id.button_select_root:
|
||||||
|
mView.launchFileListActivity(REQUEST_SELECT_CITRA_DIRECTORY);
|
||||||
|
return true;
|
||||||
|
|
||||||
case R.id.button_add_directory:
|
case R.id.button_add_directory:
|
||||||
launchFileListActivity(REQUEST_ADD_DIRECTORY);
|
launchFileListActivity(REQUEST_ADD_DIRECTORY);
|
||||||
return true;
|
return true;
|
||||||
@ -74,9 +81,12 @@ public final class MainPresenter {
|
|||||||
mDirToAdd = dir;
|
mDirToAdd = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refeshGameList() {
|
public void refreshGameList() {
|
||||||
GameDatabase databaseHelper = CitraApplication.databaseHelper;
|
Context context = CitraApplication.getAppContext();
|
||||||
databaseHelper.scanLibrary(databaseHelper.getWritableDatabase());
|
if (PermissionsHandler.hasWriteAccess(context)) {
|
||||||
mView.refresh();
|
GameDatabase databaseHelper = CitraApplication.databaseHelper;
|
||||||
|
databaseHelper.scanLibrary(databaseHelper.getWritableDatabase());
|
||||||
|
mView.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,18 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
import com.google.android.material.divider.MaterialDividerItemDecoration;
|
||||||
|
|
||||||
import org.citra.citra_emu.CitraApplication;
|
import org.citra.citra_emu.CitraApplication;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.adapters.GameAdapter;
|
import org.citra.citra_emu.adapters.GameAdapter;
|
||||||
@ -49,7 +55,9 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||||||
|
|
||||||
mRecyclerView.setLayoutManager(layoutManager);
|
mRecyclerView.setLayoutManager(layoutManager);
|
||||||
mRecyclerView.setAdapter(mAdapter);
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(ContextCompat.getDrawable(getActivity(), R.drawable.gamelist_divider), 1));
|
MaterialDividerItemDecoration divider = new MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL);
|
||||||
|
divider.setLastItemDecorated(false);
|
||||||
|
mRecyclerView.addItemDecoration(divider);
|
||||||
|
|
||||||
// Add swipe down to refresh gesture
|
// Add swipe down to refresh gesture
|
||||||
final SwipeRefreshLayout pullToRefresh = view.findViewById(R.id.refresh_grid_games);
|
final SwipeRefreshLayout pullToRefresh = view.findViewById(R.id.refresh_grid_games);
|
||||||
@ -59,6 +67,11 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||||||
refresh();
|
refresh();
|
||||||
pullToRefresh.setRefreshing(false);
|
pullToRefresh.setRefreshing(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pullToRefresh.setProgressBackgroundColorSchemeColor(MaterialColors.getColor(pullToRefresh, R.attr.colorPrimary));
|
||||||
|
pullToRefresh.setColorSchemeColors(MaterialColors.getColor(pullToRefresh, R.attr.colorOnPrimary));
|
||||||
|
|
||||||
|
setInsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -83,4 +96,12 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||||||
mRecyclerView = root.findViewById(R.id.grid_games);
|
mRecyclerView = root.findViewById(R.id.grid_games);
|
||||||
mTextView = root.findViewById(R.id.gamelist_empty_text);
|
mTextView = root.findViewById(R.id.gamelist_empty_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setInsets() {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(mRecyclerView, (v, windowInsets) -> {
|
||||||
|
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(0, 0, 0, insets.bottom);
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import org.citra.citra_emu.dialogs.CitraDirectoryDialog;
|
||||||
|
import org.citra.citra_emu.dialogs.CopyDirProgressDialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Citra directory initialization ui flow controller.
|
||||||
|
*/
|
||||||
|
public class CitraDirectoryHelper {
|
||||||
|
public interface Listener {
|
||||||
|
void onDirectoryInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FragmentActivity mFragmentActivity;
|
||||||
|
private final Listener mListener;
|
||||||
|
|
||||||
|
public CitraDirectoryHelper(FragmentActivity mFragmentActivity, Listener mListener) {
|
||||||
|
this.mFragmentActivity = mFragmentActivity;
|
||||||
|
this.mListener = mListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showCitraDirectoryDialog(Uri result) {
|
||||||
|
CitraDirectoryDialog citraDirectoryDialog = CitraDirectoryDialog.newInstance(
|
||||||
|
result.toString(), ((moveData, path) -> {
|
||||||
|
Uri previous = PermissionsHandler.getCitraDirectory();
|
||||||
|
// Do noting if user select the previous path.
|
||||||
|
if (path.equals(previous)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int takeFlags = (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
mFragmentActivity.getContentResolver().takePersistableUriPermission(path,
|
||||||
|
takeFlags);
|
||||||
|
if (!moveData || previous == null) {
|
||||||
|
initializeCitraDirectory(path);
|
||||||
|
mListener.onDirectoryInitialized();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user check move data, show copy progress dialog.
|
||||||
|
showCopyDialog(previous, path);
|
||||||
|
}));
|
||||||
|
citraDirectoryDialog.show(mFragmentActivity.getSupportFragmentManager(),
|
||||||
|
CitraDirectoryDialog.TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showCopyDialog(Uri previous, Uri path) {
|
||||||
|
CopyDirProgressDialog copyDirProgressDialog = new CopyDirProgressDialog();
|
||||||
|
copyDirProgressDialog.showNow(mFragmentActivity.getSupportFragmentManager(),
|
||||||
|
CopyDirProgressDialog.TAG);
|
||||||
|
|
||||||
|
// Run copy dir in background
|
||||||
|
Executors.newSingleThreadExecutor().execute(() -> {
|
||||||
|
FileUtil.copyDir(
|
||||||
|
mFragmentActivity, previous.toString(), path.toString(),
|
||||||
|
new FileUtil.CopyDirListener() {
|
||||||
|
@Override
|
||||||
|
public void onSearchProgress(String directoryName) {
|
||||||
|
copyDirProgressDialog.onUpdateSearchProgress(directoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCopyProgress(String filename, int progress, int max) {
|
||||||
|
copyDirProgressDialog.onUpdateCopyProgress(filename, progress, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
initializeCitraDirectory(path);
|
||||||
|
copyDirProgressDialog.dismissAllowingStateLoss();
|
||||||
|
mListener.onDirectoryInitialized();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeCitraDirectory(Uri path) {
|
||||||
|
if (!PermissionsHandler.setCitraDirectory(path.toString()))
|
||||||
|
return;
|
||||||
|
DirectoryInitialization.resetCitraDirectoryState();
|
||||||
|
DirectoryInitialization.start(mFragmentActivity);
|
||||||
|
}
|
||||||
|
}
|
@ -9,19 +9,18 @@ package org.citra.citra_emu.utils;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.citra.citra_emu.NativeLibrary;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.citra.citra_emu.CitraApplication;
|
||||||
|
import org.citra.citra_emu.NativeLibrary;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service that spawns its own thread in order to copy several binary and shader files
|
* A service that spawns its own thread in order to copy several binary and shader files
|
||||||
@ -49,6 +48,9 @@ public final class DirectoryInitialization {
|
|||||||
if (PermissionsHandler.hasWriteAccess(context)) {
|
if (PermissionsHandler.hasWriteAccess(context)) {
|
||||||
if (setCitraUserDirectory()) {
|
if (setCitraUserDirectory()) {
|
||||||
initializeInternalStorage(context);
|
initializeInternalStorage(context);
|
||||||
|
CitraApplication.documentsTree.setRoot(Uri.parse(userPath));
|
||||||
|
NativeLibrary.CreateLogFile();
|
||||||
|
NativeLibrary.LogUserDirectory(userPath);
|
||||||
NativeLibrary.CreateConfigFile();
|
NativeLibrary.CreateConfigFile();
|
||||||
directoryState = DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED;
|
directoryState = DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED;
|
||||||
} else {
|
} else {
|
||||||
@ -75,6 +77,11 @@ public final class DirectoryInitialization {
|
|||||||
return directoryState == DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED;
|
return directoryState == DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void resetCitraDirectoryState() {
|
||||||
|
directoryState = null;
|
||||||
|
isCitraDirectoryInitializationRunning.compareAndSet(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
public static String getUserDirectory() {
|
public static String getUserDirectory() {
|
||||||
if (directoryState == null) {
|
if (directoryState == null) {
|
||||||
throw new IllegalStateException("DirectoryInitialization has to run at least once!");
|
throw new IllegalStateException("DirectoryInitialization has to run at least once!");
|
||||||
@ -88,15 +95,11 @@ public final class DirectoryInitialization {
|
|||||||
private static native void SetSysDirectory(String path);
|
private static native void SetSysDirectory(String path);
|
||||||
|
|
||||||
private static boolean setCitraUserDirectory() {
|
private static boolean setCitraUserDirectory() {
|
||||||
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
|
Uri dataPath = PermissionsHandler.getCitraDirectory();
|
||||||
File externalPath = Environment.getExternalStorageDirectory();
|
if (dataPath != null) {
|
||||||
if (externalPath != null) {
|
userPath = dataPath.toString();
|
||||||
userPath = externalPath.getAbsolutePath() + "/citra-emu";
|
Log.debug("[DirectoryInitialization] User Dir: " + userPath);
|
||||||
Log.debug("[DirectoryInitialization] User Dir: " + userPath);
|
return true;
|
||||||
// NativeLibrary.SetUserDirectory(userPath);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -0,0 +1,271 @@
|
|||||||
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
|
import org.citra.citra_emu.CitraApplication;
|
||||||
|
import org.citra.citra_emu.model.CheapDocument;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cached document tree for citra user directory.
|
||||||
|
* For every filepath which is not startsWith "content://" will need to use this class to traverse.
|
||||||
|
* For example:
|
||||||
|
* C++ citra log file directory will be /log/citra_log.txt.
|
||||||
|
* After DocumentsTree.resolvePath() it will become content URI.
|
||||||
|
*/
|
||||||
|
public class DocumentsTree {
|
||||||
|
private DocumentsNode root;
|
||||||
|
private final Context context;
|
||||||
|
public static final String DELIMITER = "/";
|
||||||
|
|
||||||
|
public DocumentsTree() {
|
||||||
|
context = CitraApplication.getAppContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoot(Uri rootUri) {
|
||||||
|
root = null;
|
||||||
|
root = new DocumentsNode();
|
||||||
|
root.uri = rootUri;
|
||||||
|
root.isDirectory = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createFile(String filepath, String name) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) return false;
|
||||||
|
if (!node.isDirectory) return false;
|
||||||
|
if (!node.loaded) structTree(node);
|
||||||
|
Uri mUri = node.uri;
|
||||||
|
try {
|
||||||
|
String filename = URLDecoder.decode(name, FileUtil.DECODE_METHOD);
|
||||||
|
if (node.children.get(filename) != null) return true;
|
||||||
|
DocumentFile createdFile = FileUtil.createFile(context, mUri.toString(), name);
|
||||||
|
if (createdFile == null) return false;
|
||||||
|
DocumentsNode document = new DocumentsNode(createdFile, false);
|
||||||
|
document.parent = node;
|
||||||
|
node.children.put(document.key, document);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[DocumentsTree]: Cannot create file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createDir(String filepath, String name) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) return false;
|
||||||
|
if (!node.isDirectory) return false;
|
||||||
|
if (!node.loaded) structTree(node);
|
||||||
|
Uri mUri = node.uri;
|
||||||
|
try {
|
||||||
|
String filename = URLDecoder.decode(name, FileUtil.DECODE_METHOD);
|
||||||
|
if (node.children.get(filename) != null) return true;
|
||||||
|
DocumentFile createdDirectory = FileUtil.createDir(context, mUri.toString(), name);
|
||||||
|
if (createdDirectory == null) return false;
|
||||||
|
DocumentsNode document = new DocumentsNode(createdDirectory, true);
|
||||||
|
document.parent = node;
|
||||||
|
node.children.put(document.key, document);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[DocumentsTree]: Cannot create file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int openContentUri(String filepath, String openmode) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return FileUtil.openContentUri(context, node.uri.toString(), openmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename(String filepath) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return node.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getFilesName(String filepath) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null || !node.isDirectory) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
// If this directory have not been iterate struct it.
|
||||||
|
if (!node.loaded) structTree(node);
|
||||||
|
return node.children.keySet().toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFileSize(String filepath) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null || node.isDirectory) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return FileUtil.getFileSize(context, node.uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory(String filepath) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) return false;
|
||||||
|
return node.isDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean Exists(String filepath) {
|
||||||
|
return resolvePath(filepath) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean copyFile(String sourcePath, String destinationParentPath, String destinationFilename) {
|
||||||
|
DocumentsNode sourceNode = resolvePath(sourcePath);
|
||||||
|
if (sourceNode == null) return false;
|
||||||
|
DocumentsNode destinationNode = resolvePath(destinationParentPath);
|
||||||
|
if (destinationNode == null) return false;
|
||||||
|
try {
|
||||||
|
DocumentFile destinationParent = DocumentFile.fromTreeUri(context, destinationNode.uri);
|
||||||
|
if (destinationParent == null) return false;
|
||||||
|
String filename = URLDecoder.decode(destinationFilename, "UTF-8");
|
||||||
|
DocumentFile destination = destinationParent.createFile("application/octet-stream", filename);
|
||||||
|
if (destination == null) return false;
|
||||||
|
DocumentsNode document = new DocumentsNode();
|
||||||
|
document.uri = destination.getUri();
|
||||||
|
document.parent = destinationNode;
|
||||||
|
document.name = destination.getName();
|
||||||
|
document.isDirectory = destination.isDirectory();
|
||||||
|
document.loaded = true;
|
||||||
|
InputStream input = context.getContentResolver().openInputStream(sourceNode.uri);
|
||||||
|
OutputStream output = context.getContentResolver().openOutputStream(destination.getUri());
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = input.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
input.close();
|
||||||
|
output.flush();
|
||||||
|
output.close();
|
||||||
|
destinationNode.children.put(document.key, document);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[DocumentsTree]: Cannot copy file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean renameFile(String filepath, String destinationFilename) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) return false;
|
||||||
|
try {
|
||||||
|
Uri mUri = node.uri;
|
||||||
|
String filename = URLDecoder.decode(destinationFilename, FileUtil.DECODE_METHOD);
|
||||||
|
DocumentsContract.renameDocument(context.getContentResolver(), mUri, filename);
|
||||||
|
node.rename(filename);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[DocumentsTree]: Cannot rename file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteDocument(String filepath) {
|
||||||
|
DocumentsNode node = resolvePath(filepath);
|
||||||
|
if (node == null) return false;
|
||||||
|
try {
|
||||||
|
Uri mUri = node.uri;
|
||||||
|
if (!DocumentsContract.deleteDocument(context.getContentResolver(), mUri)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (node.parent != null) {
|
||||||
|
node.parent.children.remove(node.key);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[DocumentsTree]: Cannot rename file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private DocumentsNode resolvePath(String filepath) {
|
||||||
|
if (root == null)
|
||||||
|
return null;
|
||||||
|
StringTokenizer tokens = new StringTokenizer(filepath, DELIMITER, false);
|
||||||
|
DocumentsNode iterator = root;
|
||||||
|
while (tokens.hasMoreTokens()) {
|
||||||
|
String token = tokens.nextToken();
|
||||||
|
if (token.isEmpty()) continue;
|
||||||
|
iterator = find(iterator, token);
|
||||||
|
if (iterator == null) return null;
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private DocumentsNode find(DocumentsNode parent, String filename) {
|
||||||
|
if (parent.isDirectory && !parent.loaded) {
|
||||||
|
structTree(parent);
|
||||||
|
}
|
||||||
|
return parent.children.get(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct current level directory tree
|
||||||
|
*
|
||||||
|
* @param parent parent node of this level
|
||||||
|
*/
|
||||||
|
private void structTree(DocumentsNode parent) {
|
||||||
|
CheapDocument[] documents = FileUtil.listFiles(context, parent.uri);
|
||||||
|
for (CheapDocument document : documents) {
|
||||||
|
DocumentsNode node = new DocumentsNode(document);
|
||||||
|
node.parent = parent;
|
||||||
|
parent.children.put(node.key, node);
|
||||||
|
}
|
||||||
|
parent.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DocumentsNode {
|
||||||
|
private DocumentsNode parent;
|
||||||
|
private final Map<String, DocumentsNode> children = new HashMap<>();
|
||||||
|
private String key;
|
||||||
|
private String name;
|
||||||
|
private Uri uri;
|
||||||
|
private boolean loaded = false;
|
||||||
|
private boolean isDirectory = false;
|
||||||
|
|
||||||
|
private DocumentsNode() {}
|
||||||
|
|
||||||
|
private DocumentsNode(CheapDocument document) {
|
||||||
|
name = document.getFilename();
|
||||||
|
uri = document.getUri();
|
||||||
|
key = FileUtil.getFilenameWithExtensions(uri);
|
||||||
|
isDirectory = document.isDirectory();
|
||||||
|
loaded = !isDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DocumentsNode(DocumentFile document, boolean isCreateDir) {
|
||||||
|
name = document.getName();
|
||||||
|
uri = document.getUri();
|
||||||
|
key = FileUtil.getFilenameWithExtensions(uri);
|
||||||
|
isDirectory = isCreateDir;
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rename(String key) {
|
||||||
|
if (parent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent.children.remove(this.key);
|
||||||
|
this.name = key;
|
||||||
|
parent.children.put(key, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,71 +1,48 @@
|
|||||||
package org.citra.citra_emu.utils;
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
import java.util.ArrayList;
|
||||||
import com.nononsenseapps.filepicker.Utils;
|
|
||||||
|
|
||||||
import org.citra.citra_emu.activities.CustomFilePickerActivity;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class FileBrowserHelper {
|
public final class FileBrowserHelper {
|
||||||
public static void openDirectoryPicker(FragmentActivity activity, int requestCode, int title, List<String> extensions) {
|
|
||||||
Intent i = new Intent(activity, CustomFilePickerActivity.class);
|
|
||||||
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_START_PATH,
|
|
||||||
Environment.getExternalStorageDirectory().getPath());
|
|
||||||
i.putExtra(CustomFilePickerActivity.EXTRA_TITLE, title);
|
|
||||||
i.putExtra(CustomFilePickerActivity.EXTRA_EXTENSIONS, String.join(",", extensions));
|
|
||||||
|
|
||||||
activity.startActivityForResult(i, requestCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openFilePicker(FragmentActivity activity, int requestCode, int title,
|
|
||||||
List<String> extensions, boolean allowMultiple) {
|
|
||||||
Intent i = new Intent(activity, CustomFilePickerActivity.class);
|
|
||||||
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, allowMultiple);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_FILE);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_START_PATH,
|
|
||||||
Environment.getExternalStorageDirectory().getPath());
|
|
||||||
i.putExtra(CustomFilePickerActivity.EXTRA_TITLE, title);
|
|
||||||
i.putExtra(CustomFilePickerActivity.EXTRA_EXTENSIONS, String.join(",", extensions));
|
|
||||||
|
|
||||||
activity.startActivityForResult(i, requestCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String getSelectedDirectory(Intent result) {
|
public static String[] getSelectedFiles(Intent result, Context context, List<String> extension) {
|
||||||
// Use the provided utility method to parse the result
|
ClipData clipData = result.getClipData();
|
||||||
List<Uri> files = Utils.getSelectedFilesFromResult(result);
|
List<DocumentFile> files = new ArrayList<>();
|
||||||
if (!files.isEmpty()) {
|
if (clipData == null) {
|
||||||
File file = Utils.getFileForUri(files.get(0));
|
files.add(DocumentFile.fromSingleUri(context, result.getData()));
|
||||||
return file.getAbsolutePath();
|
} else {
|
||||||
|
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||||
|
ClipData.Item item = clipData.getItemAt(i);
|
||||||
|
Uri uri = item.getUri();
|
||||||
|
files.add(DocumentFile.fromSingleUri(context, uri));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static String[] getSelectedFiles(Intent result) {
|
|
||||||
// Use the provided utility method to parse the result
|
|
||||||
List<Uri> files = Utils.getSelectedFilesFromResult(result);
|
|
||||||
if (!files.isEmpty()) {
|
if (!files.isEmpty()) {
|
||||||
String[] paths = new String[files.size()];
|
List<String> filePaths = new ArrayList<>();
|
||||||
for (int i = 0; i < files.size(); i++)
|
for (int i = 0; i < files.size(); i++) {
|
||||||
paths[i] = Utils.getFileForUri(files.get(i)).getAbsolutePath();
|
DocumentFile file = files.get(i);
|
||||||
return paths;
|
String filename = file.getName();
|
||||||
|
int extensionStart = filename.lastIndexOf('.');
|
||||||
|
if (extensionStart > 0) {
|
||||||
|
String fileExtension = filename.substring(extensionStart + 1);
|
||||||
|
if (extension.contains(fileExtension)) {
|
||||||
|
filePaths.add(file.getUri().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filePaths.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return filePaths.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,11 +1,385 @@
|
|||||||
package org.citra.citra_emu.utils;
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.system.Os;
|
||||||
|
import android.system.StructStatVfs;
|
||||||
|
import android.util.Pair;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.citra.citra_emu.model.CheapDocument;
|
||||||
|
|
||||||
public class FileUtil {
|
public class FileUtil {
|
||||||
|
static final String PATH_TREE = "tree";
|
||||||
|
static final String DECODE_METHOD = "UTF-8";
|
||||||
|
static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
|
||||||
|
static final String TEXT_PLAIN = "text/plain";
|
||||||
|
|
||||||
|
public interface CopyDirListener {
|
||||||
|
void onSearchProgress(String directoryName);
|
||||||
|
void onCopyProgress(String filename, int progress, int max);
|
||||||
|
|
||||||
|
void onComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a file from directory with filename.
|
||||||
|
*
|
||||||
|
* @param context Application context
|
||||||
|
* @param directory parent path for file.
|
||||||
|
* @param filename file display name.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static DocumentFile createFile(Context context, String directory, String filename) {
|
||||||
|
try {
|
||||||
|
Uri directoryUri = Uri.parse(directory);
|
||||||
|
DocumentFile parent;
|
||||||
|
parent = DocumentFile.fromTreeUri(context, directoryUri);
|
||||||
|
if (parent == null) return null;
|
||||||
|
filename = URLDecoder.decode(filename, DECODE_METHOD);
|
||||||
|
int extensionPosition = filename.lastIndexOf('.');
|
||||||
|
String extension = "";
|
||||||
|
if (extensionPosition > 0) {
|
||||||
|
extension = filename.substring(extensionPosition);
|
||||||
|
}
|
||||||
|
String mimeType = APPLICATION_OCTET_STREAM;
|
||||||
|
if (extension.equals(".txt")) {
|
||||||
|
mimeType = TEXT_PLAIN;
|
||||||
|
}
|
||||||
|
DocumentFile isExist = parent.findFile(filename);
|
||||||
|
if (isExist != null) return isExist;
|
||||||
|
return parent.createFile(mimeType, filename);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot create file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a directory from directory with filename.
|
||||||
|
*
|
||||||
|
* @param context Application context
|
||||||
|
* @param directory parent path for directory.
|
||||||
|
* @param directoryName directory display name.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static DocumentFile createDir(Context context, String directory, String directoryName) {
|
||||||
|
try {
|
||||||
|
Uri directoryUri = Uri.parse(directory);
|
||||||
|
DocumentFile parent;
|
||||||
|
parent = DocumentFile.fromTreeUri(context, directoryUri);
|
||||||
|
if (parent == null) return null;
|
||||||
|
directoryName = URLDecoder.decode(directoryName, DECODE_METHOD);
|
||||||
|
DocumentFile isExist = parent.findFile(directoryName);
|
||||||
|
if (isExist != null) return isExist;
|
||||||
|
return parent.createDirectory(directoryName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot create file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open content uri and return file descriptor to JNI.
|
||||||
|
*
|
||||||
|
* @param context Application context
|
||||||
|
* @param path Native content uri path
|
||||||
|
* @param openmode will be one of "r", "r", "rw", "wa", "rwa"
|
||||||
|
* @return file descriptor
|
||||||
|
*/
|
||||||
|
public static int openContentUri(Context context, String path, String openmode) {
|
||||||
|
try (ParcelFileDescriptor parcelFileDescriptor =
|
||||||
|
context.getContentResolver().openFileDescriptor(Uri.parse(path), openmode)) {
|
||||||
|
if (parcelFileDescriptor == null) {
|
||||||
|
Log.error("[FileUtil]: Cannot get the file descriptor from uri: " + path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return parcelFileDescriptor.detachFd();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot open content uri, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
||||||
|
* This function will be faster than DocumentFile.listFiles
|
||||||
|
*
|
||||||
|
* @param context Application context
|
||||||
|
* @param uri Directory uri.
|
||||||
|
* @return CheapDocument lists.
|
||||||
|
*/
|
||||||
|
public static CheapDocument[] listFiles(Context context, Uri uri) {
|
||||||
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
|
final String[] columns = new String[]{
|
||||||
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||||
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
||||||
|
DocumentsContract.Document.COLUMN_MIME_TYPE,
|
||||||
|
};
|
||||||
|
Cursor c = null;
|
||||||
|
final List<CheapDocument> results = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
String docId;
|
||||||
|
if (isRootTreeUri(uri)) {
|
||||||
|
docId = DocumentsContract.getTreeDocumentId(uri);
|
||||||
|
} else {
|
||||||
|
docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
}
|
||||||
|
final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId);
|
||||||
|
c = resolver.query(childrenUri, columns, null, null, null);
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
final String documentId = c.getString(0);
|
||||||
|
final String documentName = c.getString(1);
|
||||||
|
final String documentMimeType = c.getString(2);
|
||||||
|
final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId);
|
||||||
|
CheapDocument document = new CheapDocument(documentName, documentMimeType, documentUri);
|
||||||
|
results.add(document);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot list file error: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
closeQuietly(c);
|
||||||
|
}
|
||||||
|
return results.toArray(new CheapDocument[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether given path exists.
|
||||||
|
*
|
||||||
|
* @param path Native content uri path
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static boolean Exists(Context context, String path) {
|
||||||
|
Cursor c = null;
|
||||||
|
try {
|
||||||
|
Uri mUri = Uri.parse(path);
|
||||||
|
final String[] columns = new String[] {DocumentsContract.Document.COLUMN_DOCUMENT_ID};
|
||||||
|
c = context.getContentResolver().query(mUri, columns, null, null, null);
|
||||||
|
return c.getCount() > 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.info("[FileUtil] Cannot find file from given path, error: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
closeQuietly(c);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether given path is a directory
|
||||||
|
*
|
||||||
|
* @param path content uri path
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static boolean isDirectory(Context context, String path) {
|
||||||
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
|
final String[] columns = new String[] {DocumentsContract.Document.COLUMN_MIME_TYPE};
|
||||||
|
boolean isDirectory = false;
|
||||||
|
Cursor c = null;
|
||||||
|
try {
|
||||||
|
Uri mUri = Uri.parse(path);
|
||||||
|
c = resolver.query(mUri, columns, null, null, null);
|
||||||
|
c.moveToNext();
|
||||||
|
final String mimeType = c.getString(0);
|
||||||
|
isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot list files, error: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
closeQuietly(c);
|
||||||
|
}
|
||||||
|
return isDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file display name from given path
|
||||||
|
*
|
||||||
|
* @param path content uri path
|
||||||
|
* @return String display name
|
||||||
|
*/
|
||||||
|
public static String getFilename(Context context, String path) {
|
||||||
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
|
final String[] columns = new String[] {DocumentsContract.Document.COLUMN_DISPLAY_NAME};
|
||||||
|
String filename = "";
|
||||||
|
Cursor c = null;
|
||||||
|
try {
|
||||||
|
Uri mUri = Uri.parse(path);
|
||||||
|
c = resolver.query(mUri, columns, null, null, null);
|
||||||
|
c.moveToNext();
|
||||||
|
filename = c.getString(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot get file size, error: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
closeQuietly(c);
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] getFilesName(Context context, String path) {
|
||||||
|
Uri uri = Uri.parse(path);
|
||||||
|
List<String> files = new ArrayList<>();
|
||||||
|
for (CheapDocument file : FileUtil.listFiles(context, uri)) {
|
||||||
|
files.add(file.getFilename());
|
||||||
|
}
|
||||||
|
return files.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file size from given path.
|
||||||
|
*
|
||||||
|
* @param path content uri path
|
||||||
|
* @return long file size
|
||||||
|
*/
|
||||||
|
public static long getFileSize(Context context, String path) {
|
||||||
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
|
final String[] columns = new String[] {DocumentsContract.Document.COLUMN_SIZE};
|
||||||
|
long size = 0;
|
||||||
|
Cursor c = null;
|
||||||
|
try {
|
||||||
|
Uri mUri = Uri.parse(path);
|
||||||
|
c = resolver.query(mUri, columns, null, null, null);
|
||||||
|
c.moveToNext();
|
||||||
|
size = c.getLong(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot get file size, error: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
closeQuietly(c);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copyFile(Context context, String sourcePath, String destinationParentPath, String destinationFilename) {
|
||||||
|
try {
|
||||||
|
Uri sourceUri = Uri.parse(sourcePath);
|
||||||
|
Uri destinationUri = Uri.parse(destinationParentPath);
|
||||||
|
DocumentFile destinationParent = DocumentFile.fromTreeUri(context, destinationUri);
|
||||||
|
if (destinationParent == null) return false;
|
||||||
|
String filename = URLDecoder.decode(destinationFilename, "UTF-8");
|
||||||
|
DocumentFile destination = destinationParent.findFile(filename);
|
||||||
|
if (destination == null) {
|
||||||
|
destination = destinationParent.createFile("application/octet-stream", filename);
|
||||||
|
}
|
||||||
|
if (destination == null) return false;
|
||||||
|
InputStream input = context.getContentResolver().openInputStream(sourceUri);
|
||||||
|
OutputStream output = context.getContentResolver().openOutputStream(destination.getUri());
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = input.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
input.close();
|
||||||
|
output.flush();
|
||||||
|
output.close();
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot copy file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyDir(Context context, String sourcePath, String destinationPath,
|
||||||
|
CopyDirListener listener) {
|
||||||
|
try {
|
||||||
|
Uri sourceUri = Uri.parse(sourcePath);
|
||||||
|
Uri destinationUri = Uri.parse(destinationPath);
|
||||||
|
final List<Pair<CheapDocument, DocumentFile>> files = new ArrayList<>();
|
||||||
|
final List<Pair<Uri, Uri>> dirs = new ArrayList<>();
|
||||||
|
dirs.add(new Pair<>(sourceUri, destinationUri));
|
||||||
|
// Searching all files which need to be copied and struct the directory in destination.
|
||||||
|
while (!dirs.isEmpty()) {
|
||||||
|
DocumentFile fromDir = DocumentFile.fromTreeUri(context, dirs.get(0).first);
|
||||||
|
DocumentFile toDir = DocumentFile.fromTreeUri(context, dirs.get(0).second);
|
||||||
|
if (fromDir == null || toDir == null)
|
||||||
|
continue;
|
||||||
|
Uri fromUri = fromDir.getUri();
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onSearchProgress(fromUri.getPath());
|
||||||
|
}
|
||||||
|
CheapDocument[] documents = FileUtil.listFiles(context, fromUri);
|
||||||
|
for (CheapDocument document : documents) {
|
||||||
|
String filename = document.getFilename();
|
||||||
|
if (document.isDirectory()) {
|
||||||
|
DocumentFile target = toDir.findFile(filename);
|
||||||
|
if (target == null || !target.exists()) {
|
||||||
|
target = toDir.createDirectory(filename);
|
||||||
|
}
|
||||||
|
if (target == null)
|
||||||
|
continue;
|
||||||
|
dirs.add(new Pair<>(document.getUri(), target.getUri()));
|
||||||
|
} else {
|
||||||
|
DocumentFile target = toDir.findFile(filename);
|
||||||
|
if (target == null || !target.exists()) {
|
||||||
|
target =
|
||||||
|
toDir.createFile(document.getMimeType(), document.getFilename());
|
||||||
|
}
|
||||||
|
if (target == null)
|
||||||
|
continue;
|
||||||
|
files.add(new Pair<>(document, target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int total = files.size();
|
||||||
|
int progress = 0;
|
||||||
|
for (Pair<CheapDocument, DocumentFile> file : files) {
|
||||||
|
DocumentFile to = file.second;
|
||||||
|
Uri toUri = to.getUri();
|
||||||
|
String filename = getFilenameWithExtensions(toUri);
|
||||||
|
String toPath = toUri.getPath();
|
||||||
|
DocumentFile toParent = to.getParentFile();
|
||||||
|
if (toParent == null)
|
||||||
|
continue;
|
||||||
|
FileUtil.copyFile(context, file.first.getUri().toString(),
|
||||||
|
toParent.getUri().toString(), filename);
|
||||||
|
progress++;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onCopyProgress(toPath, progress, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onComplete();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot copy directory, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean renameFile(Context context, String path, String destinationFilename) {
|
||||||
|
try {
|
||||||
|
Uri uri = Uri.parse(path);
|
||||||
|
DocumentsContract.renameDocument(context.getContentResolver(), uri, destinationFilename);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot rename file, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean deleteDocument(Context context, String path) {
|
||||||
|
try {
|
||||||
|
Uri uri = Uri.parse(path);
|
||||||
|
DocumentsContract.deleteDocument(context.getContentResolver(), uri);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil]: Cannot delete document, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] getBytesFromFile(File file) throws IOException {
|
public static byte[] getBytesFromFile(File file) throws IOException {
|
||||||
final long length = file.length();
|
final long length = file.length();
|
||||||
|
|
||||||
@ -21,8 +395,8 @@ public class FileUtil {
|
|||||||
int numRead;
|
int numRead;
|
||||||
|
|
||||||
try (InputStream is = new FileInputStream(file)) {
|
try (InputStream is = new FileInputStream(file)) {
|
||||||
while (offset < bytes.length
|
while (offset < bytes.length &&
|
||||||
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
|
(numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
|
||||||
offset += numRead;
|
offset += numRead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,4 +408,53 @@ public class FileUtil {
|
|||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isRootTreeUri(Uri uri) {
|
||||||
|
final List<String> paths = uri.getPathSegments();
|
||||||
|
return paths.size() == 2 && PATH_TREE.equals(paths.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNativePath(String path) {
|
||||||
|
try {
|
||||||
|
return path.charAt(0) == '/';
|
||||||
|
} catch (StringIndexOutOfBoundsException e) {
|
||||||
|
Log.error("[FileUtil] Cannot determine the string is native path or not.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFilenameWithExtensions(Uri uri) {
|
||||||
|
final String path = uri.getPath();
|
||||||
|
final int index = path.lastIndexOf('/');
|
||||||
|
return path.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getFreeSpace(Context context, Uri uri) {
|
||||||
|
try {
|
||||||
|
Uri docTreeUri = DocumentsContract.buildDocumentUriUsingTree(
|
||||||
|
uri, DocumentsContract.getTreeDocumentId(uri));
|
||||||
|
ParcelFileDescriptor pfd =
|
||||||
|
context.getContentResolver().openFileDescriptor(docTreeUri, "r");
|
||||||
|
assert pfd != null;
|
||||||
|
StructStatVfs stats = Os.fstatvfs(pfd.getFileDescriptor());
|
||||||
|
double spaceInGigaBytes = stats.f_bavail * stats.f_bsize / 1024.0 / 1024 / 1024;
|
||||||
|
pfd.close();
|
||||||
|
return spaceInGigaBytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[FileUtil] Cannot get storage size.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void closeQuietly(AutoCloseable closeable) {
|
||||||
|
if (closeable != null) {
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
} catch (RuntimeException rethrown) {
|
||||||
|
throw rethrown;
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class ForegroundService extends Service {
|
|||||||
private void showRunningNotification() {
|
private void showRunningNotification() {
|
||||||
// Intent is used to resume emulation if the notification is clicked
|
// Intent is used to resume emulation if the notification is clicked
|
||||||
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||||
new Intent(this, EmulationActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
new Intent(this, EmulationActivity.class), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.app_notification_channel_id))
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.app_notification_channel_id))
|
||||||
.setSmallIcon(R.drawable.ic_stat_notification_logo)
|
.setSmallIcon(R.drawable.ic_stat_notification_logo)
|
||||||
|
@ -13,12 +13,12 @@ import java.nio.IntBuffer;
|
|||||||
public class GameIconRequestHandler extends RequestHandler {
|
public class GameIconRequestHandler extends RequestHandler {
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandleRequest(Request data) {
|
public boolean canHandleRequest(Request data) {
|
||||||
return "iso".equals(data.uri.getScheme());
|
return "content".equals(data.uri.getScheme()) || data.uri.getScheme() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result load(Request request, int networkPolicy) {
|
public Result load(Request request, int networkPolicy) {
|
||||||
String url = request.uri.getHost() + request.uri.getPath();
|
String url = request.uri.toString();
|
||||||
int[] vector = NativeLibrary.GetIcon(url);
|
int[] vector = NativeLibrary.GetIcon(url);
|
||||||
Bitmap bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.RGB_565);
|
Bitmap bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.RGB_565);
|
||||||
bitmap.copyPixelsFromBuffer(IntBuffer.wrap(vector));
|
bitmap.copyPixelsFromBuffer(IntBuffer.wrap(vector));
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
|
||||||
|
public class InsetsHelper {
|
||||||
|
public static final int THREE_BUTTON_NAVIGATION = 0;
|
||||||
|
public static final int TWO_BUTTON_NAVIGATION = 1;
|
||||||
|
public static final int GESTURE_NAVIGATION = 2;
|
||||||
|
|
||||||
|
public static void insetAppBar(Insets insets, AppBarLayout appBarLayout)
|
||||||
|
{
|
||||||
|
ViewGroup.MarginLayoutParams mlpAppBar =
|
||||||
|
(ViewGroup.MarginLayoutParams) appBarLayout.getLayoutParams();
|
||||||
|
mlpAppBar.leftMargin = insets.left;
|
||||||
|
mlpAppBar.rightMargin = insets.right;
|
||||||
|
appBarLayout.setLayoutParams(mlpAppBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSystemGestureType(Context context) {
|
||||||
|
Resources resources = context.getResources();
|
||||||
|
int resourceId = resources.getIdentifier("config_navBarInteractionMode", "integer", "android");
|
||||||
|
if (resourceId != 0) {
|
||||||
|
return resources.getInteger(resourceId);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,32 @@
|
|||||||
package org.citra.citra_emu.utils;
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
import org.citra.citra_emu.CitraApplication;
|
||||||
|
import org.citra.citra_emu.R;
|
||||||
|
|
||||||
public class PermissionsHandler {
|
public class PermissionsHandler {
|
||||||
public static final int REQUEST_CODE_WRITE_PERMISSION = 500;
|
public static final String CITRA_DIRECTORY = "CITRA_DIRECTORY";
|
||||||
|
public static final SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext());
|
||||||
|
|
||||||
// We use permissions acceptance as an indicator if this is a first boot for the user.
|
// We use permissions acceptance as an indicator if this is a first boot for the user.
|
||||||
public static boolean isFirstBoot(final FragmentActivity activity) {
|
public static boolean isFirstBoot(FragmentActivity activity) {
|
||||||
return ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED;
|
return !hasWriteAccess(activity.getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
public static boolean checkWritePermission(FragmentActivity activity,
|
||||||
public static boolean checkWritePermission(final FragmentActivity activity) {
|
ActivityResultLauncher<Uri> launcher) {
|
||||||
if (isFirstBoot(activity)) {
|
if (isFirstBoot(activity)) {
|
||||||
activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
|
launcher.launch(null);
|
||||||
REQUEST_CODE_WRITE_PERMISSION);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +34,31 @@ public class PermissionsHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasWriteAccess(Context context) {
|
public static boolean hasWriteAccess(Context context) {
|
||||||
return ContextCompat.checkSelfPermission(context, WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
try {
|
||||||
|
Uri uri = getCitraDirectory();
|
||||||
|
if (uri == null)
|
||||||
|
return false;
|
||||||
|
int takeFlags = (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
context.getContentResolver().takePersistableUriPermission(uri, takeFlags);
|
||||||
|
DocumentFile root = DocumentFile.fromTreeUri(context, uri);
|
||||||
|
if (root != null && root.exists()) return true;
|
||||||
|
context.getContentResolver().releasePersistableUriPermission(uri, takeFlags);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("[PermissionsHandler]: Cannot check citra data directory permission, error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Uri getCitraDirectory() {
|
||||||
|
String directoryString = mPreferences.getString(CITRA_DIRECTORY, "");
|
||||||
|
if (directoryString.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Uri.parse(directoryString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setCitraDirectory(String uriString) {
|
||||||
|
return mPreferences.edit().putString(CITRA_DIRECTORY, uriString).commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class PicassoUtils {
|
|||||||
public static void loadGameIcon(ImageView imageView, String gamePath) {
|
public static void loadGameIcon(ImageView imageView, String gamePath) {
|
||||||
Picasso
|
Picasso
|
||||||
.get()
|
.get()
|
||||||
.load(Uri.parse("iso:/" + gamePath))
|
.load(Uri.parse(gamePath))
|
||||||
.fit()
|
.fit()
|
||||||
.centerInside()
|
.centerInside()
|
||||||
.config(Bitmap.Config.RGB_565)
|
.config(Bitmap.Config.RGB_565)
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
package org.citra.citra_emu.utils;
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import org.citra.citra_emu.R;
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.activities.EmulationActivity;
|
import org.citra.citra_emu.activities.EmulationActivity;
|
||||||
|
|
||||||
public final class StartupHandler {
|
public final class StartupHandler {
|
||||||
private static void handlePermissionsCheck(FragmentActivity parent) {
|
private static void handlePermissionsCheck(FragmentActivity parent,
|
||||||
|
ActivityResultLauncher<Uri> launcher) {
|
||||||
// Ask the user to grant write permission if it's not already granted
|
// Ask the user to grant write permission if it's not already granted
|
||||||
PermissionsHandler.checkWritePermission(parent);
|
PermissionsHandler.checkWritePermission(parent, launcher);
|
||||||
|
|
||||||
String start_file = "";
|
String start_file = "";
|
||||||
Bundle extras = parent.getIntent().getExtras();
|
Bundle extras = parent.getIntent().getExtras();
|
||||||
@ -30,16 +34,23 @@ public final class StartupHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void HandleInit(FragmentActivity parent) {
|
public static void HandleInit(FragmentActivity parent, ActivityResultLauncher<Uri> launcher) {
|
||||||
if (PermissionsHandler.isFirstBoot(parent)) {
|
if (PermissionsHandler.isFirstBoot(parent)) {
|
||||||
// Prompt user with standard first boot disclaimer
|
// Prompt user with standard first boot disclaimer
|
||||||
new AlertDialog.Builder(parent)
|
AlertDialog dialog =
|
||||||
|
new MaterialAlertDialogBuilder(parent)
|
||||||
.setTitle(R.string.app_name)
|
.setTitle(R.string.app_name)
|
||||||
.setIcon(R.mipmap.ic_launcher)
|
.setIcon(R.mipmap.ic_launcher)
|
||||||
.setMessage(parent.getResources().getString(R.string.app_disclaimer))
|
.setMessage(R.string.app_disclaimer)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
.setOnDismissListener(dialogInterface -> handlePermissionsCheck(parent))
|
.setCancelable(false)
|
||||||
|
.setOnDismissListener(
|
||||||
|
dialogInterface -> handlePermissionsCheck(parent, launcher))
|
||||||
.show();
|
.show();
|
||||||
|
TextView textView = dialog.findViewById(android.R.id.message);
|
||||||
|
if (textView == null)
|
||||||
|
return;
|
||||||
|
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
package org.citra.citra_emu.utils;
|
package org.citra.citra_emu.utils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
|
|
||||||
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
|
||||||
import org.citra.citra_emu.CitraApplication;
|
import org.citra.citra_emu.CitraApplication;
|
||||||
|
import org.citra.citra_emu.R;
|
||||||
import org.citra.citra_emu.features.settings.utils.SettingsFile;
|
import org.citra.citra_emu.features.settings.utils.SettingsFile;
|
||||||
|
|
||||||
public class ThemeUtil {
|
public class ThemeUtil {
|
||||||
private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext());
|
private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext());
|
||||||
|
|
||||||
private static void applyTheme(int designValue) {
|
public static final float NAV_BAR_ALPHA = 0.9f;
|
||||||
|
|
||||||
|
private static void applyTheme(int designValue, AppCompatActivity activity) {
|
||||||
switch (designValue) {
|
switch (designValue) {
|
||||||
case 0:
|
case 0:
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
@ -26,9 +40,44 @@ public class ThemeUtil {
|
|||||||
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
|
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int systemReportedThemeMode = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
WindowInsetsControllerCompat windowController = WindowCompat.getInsetsController(activity.getWindow(), activity.getWindow().getDecorView());
|
||||||
|
windowController.setAppearanceLightStatusBars(systemReportedThemeMode == Configuration.UI_MODE_NIGHT_NO);
|
||||||
|
windowController.setAppearanceLightNavigationBars(systemReportedThemeMode == Configuration.UI_MODE_NIGHT_NO);
|
||||||
|
|
||||||
|
setNavigationBarColor(activity, MaterialColors.getColor(activity.getWindow().getDecorView(), R.attr.colorSurface));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyTheme() {
|
public static void applyTheme(AppCompatActivity activity) {
|
||||||
applyTheme(mPreferences.getInt(SettingsFile.KEY_DESIGN, 0));
|
applyTheme(mPreferences.getInt(SettingsFile.KEY_DESIGN, 0), activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setNavigationBarColor(@NonNull Activity activity, @ColorInt int color) {
|
||||||
|
int gestureType = InsetsHelper.getSystemGestureType(activity.getApplicationContext());
|
||||||
|
int orientation = activity.getResources().getConfiguration().orientation;
|
||||||
|
|
||||||
|
// Use a solid color when the navigation bar is on the left/right edge of the screen
|
||||||
|
if ((gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION ||
|
||||||
|
gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) &&
|
||||||
|
orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
|
activity.getWindow().setNavigationBarColor(color);
|
||||||
|
} else if (gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION ||
|
||||||
|
gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) {
|
||||||
|
// Use semi-transparent color when in portrait mode with three/two button navigation to
|
||||||
|
// partially see list items behind the navigation bar
|
||||||
|
activity.getWindow().setNavigationBarColor(ThemeUtil.getColorWithOpacity(color, NAV_BAR_ALPHA));
|
||||||
|
} else {
|
||||||
|
// Use transparent color when using gesture navigation
|
||||||
|
activity.getWindow().setNavigationBarColor(
|
||||||
|
ContextCompat.getColor(activity.getApplicationContext(),
|
||||||
|
android.R.color.transparent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
public static int getColorWithOpacity(@ColorInt int color, float alphaFactor) {
|
||||||
|
return Color.argb(Math.round(alphaFactor * Color.alpha(color)), Color.red(color),
|
||||||
|
Color.green(color), Color.blue(color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ add_library(citra-android SHARED
|
|||||||
default_ini.h
|
default_ini.h
|
||||||
emu_window/emu_window.cpp
|
emu_window/emu_window.cpp
|
||||||
emu_window/emu_window.h
|
emu_window/emu_window.h
|
||||||
|
emu_window/emu_window_gl.cpp
|
||||||
|
emu_window/emu_window_gl.h
|
||||||
emu_window/emu_window_vk.cpp
|
emu_window/emu_window_vk.cpp
|
||||||
emu_window/emu_window_vk.h
|
emu_window/emu_window_vk.h
|
||||||
game_info.cpp
|
game_info.cpp
|
||||||
@ -27,8 +29,6 @@ add_library(citra-android SHARED
|
|||||||
game_settings.h
|
game_settings.h
|
||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
id_cache.h
|
id_cache.h
|
||||||
lodepng_image_interface.cpp
|
|
||||||
lodepng_image_interface.h
|
|
||||||
mic.cpp
|
mic.cpp
|
||||||
mic.h
|
mic.h
|
||||||
native.cpp
|
native.cpp
|
||||||
@ -38,6 +38,6 @@ add_library(citra-android SHARED
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(citra-android PRIVATE audio_core common core input_common network)
|
target_link_libraries(citra-android PRIVATE audio_core common core input_common network)
|
||||||
target_link_libraries(citra-android PRIVATE android camera2ndk EGL glad inih jnigraphics lodepng log mediandk yuv)
|
target_link_libraries(citra-android PRIVATE android camera2ndk EGL glad inih jnigraphics log mediandk yuv)
|
||||||
|
|
||||||
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} citra-android)
|
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} citra-android)
|
||||||
|
@ -26,7 +26,11 @@
|
|||||||
Config::Config() {
|
Config::Config() {
|
||||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||||
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "config.ini";
|
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "config.ini";
|
||||||
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
|
std::string ini_buffer;
|
||||||
|
FileUtil::ReadFileToString(true, sdl2_config_loc, ini_buffer);
|
||||||
|
if (!ini_buffer.empty()) {
|
||||||
|
sdl2_config = std::make_unique<INIReader>(ini_buffer.c_str(), ini_buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
@ -35,12 +39,15 @@ Config::~Config() = default;
|
|||||||
|
|
||||||
bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||||
const std::string& location = this->sdl2_config_loc;
|
const std::string& location = this->sdl2_config_loc;
|
||||||
if (sdl2_config->ParseError() < 0) {
|
if (sdl2_config == nullptr || sdl2_config->ParseError() < 0) {
|
||||||
if (retry) {
|
if (retry) {
|
||||||
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
|
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
|
||||||
FileUtil::CreateFullPath(location);
|
FileUtil::CreateFullPath(location);
|
||||||
FileUtil::WriteStringToFile(true, location, default_contents);
|
FileUtil::WriteStringToFile(true, location, default_contents);
|
||||||
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
|
std::string ini_buffer;
|
||||||
|
FileUtil::ReadFileToString(true, location, ini_buffer);
|
||||||
|
sdl2_config =
|
||||||
|
std::make_unique<INIReader>(ini_buffer.c_str(), ini_buffer.size()); // Reopen file
|
||||||
|
|
||||||
return LoadINI(default_contents, false);
|
return LoadINI(default_contents, false);
|
||||||
}
|
}
|
||||||
@ -116,11 +123,11 @@ void Config::ReadValues() {
|
|||||||
// Renderer
|
// Renderer
|
||||||
Settings::values.graphics_api =
|
Settings::values.graphics_api =
|
||||||
static_cast<Settings::GraphicsAPI>(sdl2_config->GetInteger("Renderer", "graphics_api", 2));
|
static_cast<Settings::GraphicsAPI>(sdl2_config->GetInteger("Renderer", "graphics_api", 2));
|
||||||
Settings::values.async_command_recording =
|
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "renderer_debug", false);
|
||||||
sdl2_config->GetBoolean("Renderer", "async_command_recording", true);
|
Settings::values.async_shader_compilation =
|
||||||
|
sdl2_config->GetBoolean("Renderer", "async_shader_compilation", true);
|
||||||
Settings::values.spirv_shader_gen =
|
Settings::values.spirv_shader_gen =
|
||||||
sdl2_config->GetBoolean("Renderer", "spirv_shader_gen", true);
|
sdl2_config->GetBoolean("Renderer", "spirv_shader_gen", true);
|
||||||
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "renderer_debug", false);
|
|
||||||
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
|
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
|
||||||
Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true);
|
Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true);
|
||||||
Settings::values.shaders_accurate_mul =
|
Settings::values.shaders_accurate_mul =
|
||||||
|
@ -98,9 +98,21 @@ use_cpu_jit =
|
|||||||
cpu_clock_percentage =
|
cpu_clock_percentage =
|
||||||
|
|
||||||
[Renderer]
|
[Renderer]
|
||||||
# Whether to render using GLES or OpenGL
|
# Whether to render using OpenGL or Vulkan
|
||||||
# 0: OpenGL, 1 (default): GLES
|
# 1: OpenGL, 2 (default): Vulkan
|
||||||
use_gles =
|
graphics_api =
|
||||||
|
|
||||||
|
# Whether to emit PICA fragment shader using SPIRV or GLSL
|
||||||
|
# 1: SPIR-V (default), 0: GLSL
|
||||||
|
spirv_shader_gen =
|
||||||
|
|
||||||
|
# Whether to use a worker thread for vulkan command buffer recording
|
||||||
|
# 1: On (default), 0: Off
|
||||||
|
async_command_recording =
|
||||||
|
|
||||||
|
# Whether to enable additional debugging information during emulation
|
||||||
|
# 1: On, 0 (default): Off
|
||||||
|
renderer_debug =
|
||||||
|
|
||||||
# Whether to use software or hardware rendering.
|
# Whether to use software or hardware rendering.
|
||||||
# 0: Software, 1 (default): Hardware
|
# 0: Software, 1 (default): Hardware
|
||||||
@ -168,9 +180,12 @@ factor_3d =
|
|||||||
|
|
||||||
# The name of the post processing shader to apply.
|
# The name of the post processing shader to apply.
|
||||||
# Loaded from shaders if render_3d is off or side by side.
|
# Loaded from shaders if render_3d is off or side by side.
|
||||||
# Loaded from shaders/anaglyph if render_3d is anaglyph
|
|
||||||
pp_shader_name =
|
pp_shader_name =
|
||||||
|
|
||||||
|
# The name of the shader to apply when render_3d is anaglyph.
|
||||||
|
# Loaded from shaders/anaglyph
|
||||||
|
anaglyph_shader_name =
|
||||||
|
|
||||||
# Whether to enable linear filtering or not
|
# Whether to enable linear filtering or not
|
||||||
# This is required for some shaders to work correctly
|
# This is required for some shaders to work correctly
|
||||||
# 0: Nearest, 1 (default): Linear
|
# 0: Nearest, 1 (default): Linear
|
||||||
|
@ -6,10 +6,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
@ -20,52 +17,6 @@
|
|||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
static constexpr std::array<EGLint, 15> egl_attribs{EGL_SURFACE_TYPE,
|
|
||||||
EGL_WINDOW_BIT,
|
|
||||||
EGL_RENDERABLE_TYPE,
|
|
||||||
EGL_OPENGL_ES3_BIT_KHR,
|
|
||||||
EGL_BLUE_SIZE,
|
|
||||||
8,
|
|
||||||
EGL_GREEN_SIZE,
|
|
||||||
8,
|
|
||||||
EGL_RED_SIZE,
|
|
||||||
8,
|
|
||||||
EGL_DEPTH_SIZE,
|
|
||||||
0,
|
|
||||||
EGL_STENCIL_SIZE,
|
|
||||||
0,
|
|
||||||
EGL_NONE};
|
|
||||||
static constexpr std::array<EGLint, 5> egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
|
|
||||||
static constexpr std::array<EGLint, 4> egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
|
||||||
|
|
||||||
SharedContext_Android::SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config,
|
|
||||||
EGLContext egl_share_context)
|
|
||||||
: egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config,
|
|
||||||
egl_empty_attribs.data())},
|
|
||||||
egl_context{eglCreateContext(egl_display, egl_config, egl_share_context,
|
|
||||||
egl_context_attribs.data())} {
|
|
||||||
ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!");
|
|
||||||
ASSERT_MSG(egl_context, "eglCreateContext() failed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedContext_Android::~SharedContext_Android() {
|
|
||||||
if (!eglDestroySurface(egl_display, egl_surface)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eglDestroyContext(egl_display, egl_context)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SharedContext_Android::MakeCurrent() {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SharedContext_Android::DoneCurrent() {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsPortraitMode() {
|
static bool IsPortraitMode() {
|
||||||
return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod(
|
return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod(
|
||||||
IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode());
|
IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode());
|
||||||
@ -79,6 +30,10 @@ static void UpdateLandscapeScreenLayout() {
|
|||||||
|
|
||||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||||
render_window = surface;
|
render_window = surface;
|
||||||
|
|
||||||
|
window_info.type = Frontend::WindowSystemType::Android;
|
||||||
|
window_info.render_surface = surface;
|
||||||
|
|
||||||
StopPresenting();
|
StopPresenting();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +53,7 @@ void EmuWindow_Android::OnTouchMoved(int x, int y) {
|
|||||||
void EmuWindow_Android::OnFramebufferSizeChanged() {
|
void EmuWindow_Android::OnFramebufferSizeChanged() {
|
||||||
UpdateLandscapeScreenLayout();
|
UpdateLandscapeScreenLayout();
|
||||||
const bool is_portrait_mode{IsPortraitMode()};
|
const bool is_portrait_mode{IsPortraitMode()};
|
||||||
|
|
||||||
const int bigger{window_width > window_height ? window_width : window_height};
|
const int bigger{window_width > window_height ? window_width : window_height};
|
||||||
const int smaller{window_width < window_height ? window_width : window_height};
|
const int smaller{window_width < window_height ? window_width : window_height};
|
||||||
if (is_portrait_mode) {
|
if (is_portrait_mode) {
|
||||||
@ -107,7 +63,7 @@ void EmuWindow_Android::OnFramebufferSizeChanged() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) {
|
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) : host_window{surface} {
|
||||||
LOG_DEBUG(Frontend, "Initializing EmuWindow_Android");
|
LOG_DEBUG(Frontend, "Initializing EmuWindow_Android");
|
||||||
|
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
@ -115,108 +71,10 @@ EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window_width = ANativeWindow_getWidth(surface);
|
||||||
|
window_height = ANativeWindow_getHeight(surface);
|
||||||
|
|
||||||
Network::Init();
|
Network::Init();
|
||||||
|
|
||||||
host_window = surface;
|
|
||||||
|
|
||||||
if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglGetDisplay() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglInitialize() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1,
|
|
||||||
&egl_num_configs) != EGL_TRUE) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglChooseConfig() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateWindowSurface();
|
|
||||||
|
|
||||||
if (eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &window_width) != EGL_TRUE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
|
|
||||||
egl_context == EGL_NO_CONTEXT) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglCreateContext() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) !=
|
|
||||||
EGL_TRUE) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (core_context = CreateSharedContext(); !core_context) {
|
|
||||||
LOG_CRITICAL(Frontend, "CreateSharedContext() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglMakeCurrent() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) {
|
|
||||||
LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglSwapInterval() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnFramebufferSizeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EmuWindow_Android::CreateWindowSurface() {
|
|
||||||
if (!host_window) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
EGLint format{};
|
|
||||||
eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format);
|
|
||||||
ANativeWindow_setBuffersGeometry(host_window, 0, 0, format);
|
|
||||||
|
|
||||||
if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0);
|
|
||||||
egl_surface == EGL_NO_SURFACE) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!egl_surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android::DestroyWindowSurface() {
|
|
||||||
if (!egl_surface) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}
|
|
||||||
if (!eglDestroySurface(egl_display, egl_surface)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
|
||||||
}
|
|
||||||
egl_surface = EGL_NO_SURFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android::DestroyContext() {
|
|
||||||
if (!egl_context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglGetCurrentContext() == egl_context) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}
|
|
||||||
if (!eglDestroyContext(egl_display, egl_context)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
|
||||||
}
|
|
||||||
if (!eglTerminate(egl_display)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglTerminate() failed");
|
|
||||||
}
|
|
||||||
egl_context = EGL_NO_CONTEXT;
|
|
||||||
egl_display = EGL_NO_DISPLAY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_Android::~EmuWindow_Android() {
|
EmuWindow_Android::~EmuWindow_Android() {
|
||||||
@ -224,34 +82,6 @@ EmuWindow_Android::~EmuWindow_Android() {
|
|||||||
DestroyContext();
|
DestroyContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android::CreateSharedContext() const {
|
|
||||||
return std::make_unique<SharedContext_Android>(egl_display, egl_config, egl_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android::StopPresenting() {
|
|
||||||
if (presenting_state == PresentingState::Running) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}
|
|
||||||
presenting_state = PresentingState::Stopped;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android::TryPresenting() {
|
|
||||||
if (presenting_state != PresentingState::Running) {
|
|
||||||
if (presenting_state == PresentingState::Initial) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
||||||
presenting_state = PresentingState::Running;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
|
|
||||||
if (VideoCore::g_renderer) {
|
|
||||||
VideoCore::g_renderer->TryPresent(0);
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android::PollEvents() {
|
void EmuWindow_Android::PollEvents() {
|
||||||
if (!render_window) {
|
if (!render_window) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,42 +1,17 @@
|
|||||||
// Copyright 2019 Citra Emulator Project
|
// Copyright 2022 Citra Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
|
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
|
||||||
struct ANativeWindow;
|
|
||||||
|
|
||||||
class SharedContext_Android : public Frontend::GraphicsContext {
|
|
||||||
public:
|
|
||||||
SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config,
|
|
||||||
EGLContext egl_share_context);
|
|
||||||
|
|
||||||
~SharedContext_Android() override;
|
|
||||||
|
|
||||||
void MakeCurrent() override;
|
|
||||||
|
|
||||||
void DoneCurrent() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
EGLDisplay egl_display{};
|
|
||||||
EGLSurface egl_surface{};
|
|
||||||
EGLContext egl_context{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class EmuWindow_Android : public Frontend::EmuWindow {
|
class EmuWindow_Android : public Frontend::EmuWindow {
|
||||||
public:
|
public:
|
||||||
EmuWindow_Android(ANativeWindow* surface);
|
EmuWindow_Android(ANativeWindow* surface);
|
||||||
~EmuWindow_Android();
|
~EmuWindow_Android();
|
||||||
|
|
||||||
void Present();
|
|
||||||
|
|
||||||
/// Called by the onSurfaceChanges() method to change the surface
|
/// Called by the onSurfaceChanges() method to change the surface
|
||||||
void OnSurfaceChanged(ANativeWindow* surface);
|
void OnSurfaceChanged(ANativeWindow* surface);
|
||||||
|
|
||||||
@ -47,31 +22,34 @@ public:
|
|||||||
void OnTouchMoved(int x, int y);
|
void OnTouchMoved(int x, int y);
|
||||||
|
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
|
|
||||||
void MakeCurrent() override;
|
void MakeCurrent() override;
|
||||||
|
|
||||||
void DoneCurrent() override;
|
void DoneCurrent() override;
|
||||||
|
|
||||||
void TryPresenting();
|
virtual void TryPresenting() = 0;
|
||||||
void StopPresenting();
|
|
||||||
|
|
||||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
virtual void StopPresenting() = 0;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
void OnFramebufferSizeChanged();
|
void OnFramebufferSizeChanged();
|
||||||
bool CreateWindowSurface();
|
|
||||||
void DestroyWindowSurface();
|
|
||||||
void DestroyContext();
|
|
||||||
|
|
||||||
|
/// Creates the API specific window surface
|
||||||
|
virtual bool CreateWindowSurface() {}
|
||||||
|
|
||||||
|
/// Destroys the API specific window surface
|
||||||
|
virtual void DestroyWindowSurface() {}
|
||||||
|
|
||||||
|
/// Destroys the graphics context
|
||||||
|
virtual void DestroyContext() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
ANativeWindow* render_window{};
|
ANativeWindow* render_window{};
|
||||||
ANativeWindow* host_window{};
|
ANativeWindow* host_window{};
|
||||||
|
|
||||||
int window_width{};
|
int window_width{};
|
||||||
int window_height{};
|
int window_height{};
|
||||||
|
|
||||||
EGLConfig egl_config;
|
|
||||||
EGLSurface egl_surface{};
|
|
||||||
EGLContext egl_context{};
|
|
||||||
EGLDisplay egl_display{};
|
|
||||||
|
|
||||||
std::unique_ptr<Frontend::GraphicsContext> core_context;
|
std::unique_ptr<Frontend::GraphicsContext> core_context;
|
||||||
|
|
||||||
enum class PresentingState {
|
enum class PresentingState {
|
||||||
|
205
src/android/app/src/main/jni/emu_window/emu_window_gl.cpp
Normal file
205
src/android/app/src/main/jni/emu_window/emu_window_gl.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
#include "jni/emu_window/emu_window_gl.h"
|
||||||
|
#include "jni/id_cache.h"
|
||||||
|
#include "jni/input_manager.h"
|
||||||
|
#include "network/network.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
static constexpr std::array<EGLint, 15> egl_attribs{EGL_SURFACE_TYPE,
|
||||||
|
EGL_WINDOW_BIT,
|
||||||
|
EGL_RENDERABLE_TYPE,
|
||||||
|
EGL_OPENGL_ES3_BIT_KHR,
|
||||||
|
EGL_BLUE_SIZE,
|
||||||
|
8,
|
||||||
|
EGL_GREEN_SIZE,
|
||||||
|
8,
|
||||||
|
EGL_RED_SIZE,
|
||||||
|
8,
|
||||||
|
EGL_DEPTH_SIZE,
|
||||||
|
0,
|
||||||
|
EGL_STENCIL_SIZE,
|
||||||
|
0,
|
||||||
|
EGL_NONE};
|
||||||
|
static constexpr std::array<EGLint, 5> egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
|
||||||
|
static constexpr std::array<EGLint, 4> egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
||||||
|
|
||||||
|
class SharedContext_Android : public Frontend::GraphicsContext {
|
||||||
|
public:
|
||||||
|
SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config,
|
||||||
|
EGLContext egl_share_context)
|
||||||
|
: egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config,
|
||||||
|
egl_empty_attribs.data())},
|
||||||
|
egl_context{eglCreateContext(egl_display, egl_config, egl_share_context,
|
||||||
|
egl_context_attribs.data())} {
|
||||||
|
ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!");
|
||||||
|
ASSERT_MSG(egl_context, "eglCreateContext() failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
~SharedContext_Android() override {
|
||||||
|
if (!eglDestroySurface(egl_display, egl_surface)) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eglDestroyContext(egl_display, egl_context)) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCurrent() {
|
||||||
|
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoneCurrent() {
|
||||||
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
EGLDisplay egl_display{};
|
||||||
|
EGLSurface egl_surface{};
|
||||||
|
EGLContext egl_context{};
|
||||||
|
};
|
||||||
|
|
||||||
|
EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(ANativeWindow* surface)
|
||||||
|
: EmuWindow_Android{surface} {
|
||||||
|
if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglGetDisplay() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglInitialize() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1,
|
||||||
|
&egl_num_configs) != EGL_TRUE) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglChooseConfig() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateWindowSurface();
|
||||||
|
|
||||||
|
if (eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &window_width) != EGL_TRUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
|
||||||
|
egl_context == EGL_NO_CONTEXT) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglCreateContext() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) !=
|
||||||
|
EGL_TRUE) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (core_context = CreateSharedContext(); !core_context) {
|
||||||
|
LOG_CRITICAL(Frontend, "CreateSharedContext() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglMakeCurrent() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) {
|
||||||
|
LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglSwapInterval() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnFramebufferSizeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EmuWindow_Android_OpenGL::CreateWindowSurface() {
|
||||||
|
if (!host_window) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint format{};
|
||||||
|
eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format);
|
||||||
|
ANativeWindow_setBuffersGeometry(host_window, 0, 0, format);
|
||||||
|
|
||||||
|
if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0);
|
||||||
|
egl_surface == EGL_NO_SURFACE) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!egl_surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_Android_OpenGL::DestroyWindowSurface() {
|
||||||
|
if (!egl_surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) {
|
||||||
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
if (!eglDestroySurface(egl_display, egl_surface)) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
||||||
|
}
|
||||||
|
egl_surface = EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_Android_OpenGL::DestroyContext() {
|
||||||
|
if (!egl_context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eglGetCurrentContext() == egl_context) {
|
||||||
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
if (!eglDestroyContext(egl_display, egl_context)) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
||||||
|
}
|
||||||
|
if (!eglTerminate(egl_display)) {
|
||||||
|
LOG_CRITICAL(Frontend, "eglTerminate() failed");
|
||||||
|
}
|
||||||
|
egl_context = EGL_NO_CONTEXT;
|
||||||
|
egl_display = EGL_NO_DISPLAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android_OpenGL::CreateSharedContext() const {
|
||||||
|
return std::make_unique<SharedContext_Android>(egl_display, egl_config, egl_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_Android_OpenGL::StopPresenting() {
|
||||||
|
if (presenting_state == PresentingState::Running) {
|
||||||
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
presenting_state = PresentingState::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_Android_OpenGL::TryPresenting() {
|
||||||
|
if (presenting_state != PresentingState::Running) {
|
||||||
|
if (presenting_state == PresentingState::Initial) {
|
||||||
|
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
presenting_state = PresentingState::Running;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
|
||||||
|
if (VideoCore::g_renderer) {
|
||||||
|
VideoCore::g_renderer->TryPresent(0);
|
||||||
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
|
}
|
||||||
|
}
|
36
src/android/app/src/main/jni/emu_window/emu_window_gl.h
Normal file
36
src/android/app/src/main/jni/emu_window/emu_window_gl.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
|
||||||
|
#include "jni/emu_window/emu_window.h"
|
||||||
|
|
||||||
|
struct ANativeWindow;
|
||||||
|
|
||||||
|
class EmuWindow_Android_OpenGL : public EmuWindow_Android {
|
||||||
|
public:
|
||||||
|
EmuWindow_Android_OpenGL(ANativeWindow* surface);
|
||||||
|
~EmuWindow_Android_OpenGL() override = default;
|
||||||
|
|
||||||
|
void TryPresenting() override;
|
||||||
|
void StopPresenting() override;
|
||||||
|
|
||||||
|
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool CreateWindowSurface() override;
|
||||||
|
void DestroyWindowSurface() override;
|
||||||
|
void DestroyContext() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EGLConfig egl_config;
|
||||||
|
EGLSurface egl_surface{};
|
||||||
|
EGLContext egl_context{};
|
||||||
|
EGLDisplay egl_display{};
|
||||||
|
};
|
@ -17,58 +17,10 @@
|
|||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
static bool IsPortraitMode() {
|
class SharedContext_Android : public Frontend::GraphicsContext {};
|
||||||
return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod(
|
|
||||||
IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateLandscapeScreenLayout() {
|
EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(ANativeWindow* surface)
|
||||||
Settings::values.layout_option =
|
: EmuWindow_Android{surface} {
|
||||||
static_cast<Settings::LayoutOption>(IDCache::GetEnvForThread()->CallStaticIntMethod(
|
|
||||||
IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::OnSurfaceChanged(ANativeWindow* surface) {
|
|
||||||
render_window = surface;
|
|
||||||
StopPresenting();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EmuWindow_Android_Vulkan::OnTouchEvent(int x, int y, bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
return TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchReleased();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::OnTouchMoved(int x, int y) {
|
|
||||||
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::OnFramebufferSizeChanged() {
|
|
||||||
UpdateLandscapeScreenLayout();
|
|
||||||
const bool is_portrait_mode{IsPortraitMode()};
|
|
||||||
const int bigger{window_width > window_height ? window_width : window_height};
|
|
||||||
const int smaller{window_width < window_height ? window_width : window_height};
|
|
||||||
if (is_portrait_mode) {
|
|
||||||
UpdateCurrentFramebufferLayout(smaller, bigger, is_portrait_mode);
|
|
||||||
} else {
|
|
||||||
UpdateCurrentFramebufferLayout(bigger, smaller, is_portrait_mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(ANativeWindow* surface) {
|
|
||||||
LOG_DEBUG(Frontend, "Initializing EmuWindow_Android_Vulkan");
|
|
||||||
|
|
||||||
if (!surface) {
|
|
||||||
LOG_CRITICAL(Frontend, "surface is nullptr");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::Init();
|
|
||||||
|
|
||||||
host_window = surface;
|
|
||||||
CreateWindowSurface();
|
CreateWindowSurface();
|
||||||
|
|
||||||
if (core_context = CreateSharedContext(); !core_context) {
|
if (core_context = CreateSharedContext(); !core_context) {
|
||||||
@ -90,87 +42,24 @@ bool EmuWindow_Android_Vulkan::CreateWindowSurface() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::DestroyWindowSurface() {
|
|
||||||
/*if (!egl_surface) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}
|
|
||||||
if (!eglDestroySurface(egl_display, egl_surface)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
|
||||||
}
|
|
||||||
egl_surface = EGL_NO_SURFACE;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::DestroyContext() {
|
|
||||||
/*if (!egl_context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eglGetCurrentContext() == egl_context) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}
|
|
||||||
if (!eglDestroyContext(egl_display, egl_context)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
|
|
||||||
}
|
|
||||||
if (!eglTerminate(egl_display)) {
|
|
||||||
LOG_CRITICAL(Frontend, "eglTerminate() failed");
|
|
||||||
}
|
|
||||||
egl_context = EGL_NO_CONTEXT;
|
|
||||||
egl_display = EGL_NO_DISPLAY;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
EmuWindow_Android_Vulkan::~EmuWindow_Android_Vulkan() {
|
|
||||||
DestroyWindowSurface();
|
|
||||||
DestroyContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android_Vulkan::CreateSharedContext() const {
|
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android_Vulkan::CreateSharedContext() const {
|
||||||
return std::make_unique<SharedContext_Android>();
|
return std::make_unique<SharedContext_Android>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::StopPresenting() {
|
void EmuWindow_Android_Vulkan::StopPresenting() {
|
||||||
/*if (presenting_state == PresentingState::Running) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
}*/
|
|
||||||
presenting_state = PresentingState::Stopped;
|
presenting_state = PresentingState::Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::TryPresenting() {
|
void EmuWindow_Android_Vulkan::TryPresenting() {
|
||||||
if (presenting_state != PresentingState::Running) {
|
if (presenting_state != PresentingState::Running) {
|
||||||
if (presenting_state == PresentingState::Initial) {
|
if (presenting_state == PresentingState::Initial) {
|
||||||
/*eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);*/
|
|
||||||
presenting_state = PresentingState::Running;
|
presenting_state = PresentingState::Running;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
|
|
||||||
if (VideoCore::g_renderer) {
|
if (VideoCore::g_renderer) {
|
||||||
VideoCore::g_renderer->TryPresent(0);
|
VideoCore::g_renderer->TryPresent(0);
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::PollEvents() {
|
|
||||||
if (!render_window) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
host_window = render_window;
|
|
||||||
render_window = nullptr;
|
|
||||||
|
|
||||||
DestroyWindowSurface();
|
|
||||||
CreateWindowSurface();
|
|
||||||
OnFramebufferSizeChanged();
|
|
||||||
presenting_state = PresentingState::Initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::MakeCurrent() {
|
|
||||||
core_context->MakeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_Android_Vulkan::DoneCurrent() {
|
|
||||||
core_context->DoneCurrent();
|
|
||||||
}
|
|
@ -4,56 +4,20 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include "jni/emu_window/emu_window.h"
|
||||||
#include "core/frontend/emu_window.h"
|
|
||||||
|
|
||||||
struct ANativeWindow;
|
struct ANativeWindow;
|
||||||
|
|
||||||
class SharedContext_Android : public Frontend::GraphicsContext {};
|
class EmuWindow_Android_Vulkan : public EmuWindow_Android {
|
||||||
|
|
||||||
class EmuWindow_Android_Vulkan : public Frontend::EmuWindow {
|
|
||||||
public:
|
public:
|
||||||
EmuWindow_Android_Vulkan(ANativeWindow* surface);
|
EmuWindow_Android_Vulkan(ANativeWindow* surface);
|
||||||
~EmuWindow_Android_Vulkan();
|
~EmuWindow_Android_Vulkan() override = default;
|
||||||
|
|
||||||
void Present();
|
void TryPresenting() override;
|
||||||
|
void StopPresenting() override;
|
||||||
/// Called by the onSurfaceChanges() method to change the surface
|
|
||||||
void OnSurfaceChanged(ANativeWindow* surface);
|
|
||||||
|
|
||||||
/// Handles touch event that occur.(Touched or released)
|
|
||||||
bool OnTouchEvent(int x, int y, bool pressed);
|
|
||||||
|
|
||||||
/// Handles movement of touch pointer
|
|
||||||
void OnTouchMoved(int x, int y);
|
|
||||||
|
|
||||||
void PollEvents() override;
|
|
||||||
void MakeCurrent() override;
|
|
||||||
void DoneCurrent() override;
|
|
||||||
|
|
||||||
void TryPresenting();
|
|
||||||
void StopPresenting();
|
|
||||||
|
|
||||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnFramebufferSizeChanged();
|
bool CreateWindowSurface() override;
|
||||||
bool CreateWindowSurface();
|
|
||||||
void DestroyWindowSurface();
|
|
||||||
void DestroyContext();
|
|
||||||
|
|
||||||
ANativeWindow* render_window{};
|
|
||||||
ANativeWindow* host_window{};
|
|
||||||
|
|
||||||
int window_width{1080};
|
|
||||||
int window_height{2220};
|
|
||||||
|
|
||||||
std::unique_ptr<Frontend::GraphicsContext> core_context;
|
|
||||||
|
|
||||||
enum class PresentingState {
|
|
||||||
Initial,
|
|
||||||
Running,
|
|
||||||
Stopped,
|
|
||||||
};
|
|
||||||
PresentingState presenting_state{};
|
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/android_storage.h"
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/logging/backend.h"
|
#include "common/logging/backend.h"
|
||||||
#include "common/logging/filter.h"
|
#include "common/logging/filter.h"
|
||||||
@ -159,10 +160,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||||
Log::SetGlobalFilter(log_filter);
|
Log::SetGlobalFilter(log_filter);
|
||||||
Log::AddBackend(std::make_unique<Log::LogcatBackend>());
|
Log::AddBackend(std::make_unique<Log::LogcatBackend>());
|
||||||
FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
|
||||||
Log::AddBackend(std::make_unique<Log::FileBackend>(
|
|
||||||
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + LOG_FILE));
|
|
||||||
LOG_INFO(Frontend, "Logging backend initialised");
|
|
||||||
|
|
||||||
// Initialize misc classes
|
// Initialize misc classes
|
||||||
s_savestate_info_class = reinterpret_cast<jclass>(
|
s_savestate_info_class = reinterpret_cast<jclass>(
|
||||||
@ -230,6 +227,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
MiiSelector::InitJNI(env);
|
MiiSelector::InitJNI(env);
|
||||||
SoftwareKeyboard::InitJNI(env);
|
SoftwareKeyboard::InitJNI(env);
|
||||||
Camera::StillImage::InitJNI(env);
|
Camera::StillImage::InitJNI(env);
|
||||||
|
AndroidStorage::InitJNI(env, s_native_library_class);
|
||||||
|
|
||||||
return JNI_VERSION;
|
return JNI_VERSION;
|
||||||
}
|
}
|
||||||
@ -254,6 +252,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
|||||||
MiiSelector::CleanupJNI(env);
|
MiiSelector::CleanupJNI(env);
|
||||||
SoftwareKeyboard::CleanupJNI(env);
|
SoftwareKeyboard::CleanupJNI(env);
|
||||||
Camera::StillImage::CleanupJNI(env);
|
Camera::StillImage::CleanupJNI(env);
|
||||||
|
AndroidStorage::CleanupJNI();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2019 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <lodepng.h>
|
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "jni/lodepng_image_interface.h"
|
|
||||||
|
|
||||||
bool LodePNGImageInterface::DecodePNG(std::vector<u8>& dst, u32& width, u32& height,
|
|
||||||
const std::string& path) {
|
|
||||||
u32 lodepng_ret = lodepng::decode(dst, width, height, path);
|
|
||||||
if (lodepng_ret) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to decode {} because {}", path,
|
|
||||||
lodepng_error_text(lodepng_ret));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LodePNGImageInterface::EncodePNG(const std::string& path, const std::vector<u8>& src,
|
|
||||||
u32 width, u32 height) {
|
|
||||||
u32 lodepng_ret = lodepng::encode(path, src, width, height);
|
|
||||||
if (lodepng_ret) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to encode {} because {}", path,
|
|
||||||
lodepng_error_text(lodepng_ret));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright 2019 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/frontend/image_interface.h"
|
|
||||||
|
|
||||||
class LodePNGImageInterface final : public Frontend::ImageInterface {
|
|
||||||
public:
|
|
||||||
bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, const std::string& path) override;
|
|
||||||
bool EncodePNG(const std::string& path, const std::vector<u8>& src, u32 width,
|
|
||||||
u32 height) override;
|
|
||||||
};
|
|
@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
#include "audio_core/dsp_interface.h"
|
#include "audio_core/dsp_interface.h"
|
||||||
#include "common/aarch64/cpu_detect.h"
|
#include "common/aarch64/cpu_detect.h"
|
||||||
|
#include "common/common_paths.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
@ -32,23 +34,24 @@
|
|||||||
#include "jni/camera/ndk_camera.h"
|
#include "jni/camera/ndk_camera.h"
|
||||||
#include "jni/camera/still_image_camera.h"
|
#include "jni/camera/still_image_camera.h"
|
||||||
#include "jni/config.h"
|
#include "jni/config.h"
|
||||||
|
#include "jni/emu_window/emu_window_gl.h"
|
||||||
#include "jni/emu_window/emu_window_vk.h"
|
#include "jni/emu_window/emu_window_vk.h"
|
||||||
#include "jni/game_info.h"
|
#include "jni/game_info.h"
|
||||||
#include "jni/game_settings.h"
|
#include "jni/game_settings.h"
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "jni/input_manager.h"
|
#include "jni/input_manager.h"
|
||||||
#include "jni/lodepng_image_interface.h"
|
|
||||||
#include "jni/mic.h"
|
#include "jni/mic.h"
|
||||||
#include "jni/native.h"
|
#include "jni/native.h"
|
||||||
#include "jni/ndk_motion.h"
|
#include "jni/ndk_motion.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
ANativeWindow* s_surf;
|
ANativeWindow* s_surf;
|
||||||
|
|
||||||
std::unique_ptr<EmuWindow_Android_Vulkan> window;
|
std::unique_ptr<EmuWindow_Android> window;
|
||||||
|
|
||||||
std::atomic<bool> stop_run{true};
|
std::atomic<bool> stop_run{true};
|
||||||
std::atomic<bool> pause_emulation{false};
|
std::atomic<bool> pause_emulation{false};
|
||||||
@ -146,7 +149,17 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
|||||||
return Core::System::ResultStatus::ErrorLoader;
|
return Core::System::ResultStatus::ErrorLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf);
|
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api.GetValue();
|
||||||
|
switch (graphics_api) {
|
||||||
|
case Settings::GraphicsAPI::OpenGLES:
|
||||||
|
window = std::make_unique<EmuWindow_Android_OpenGL>(s_surf);
|
||||||
|
break;
|
||||||
|
case Settings::GraphicsAPI::Vulkan:
|
||||||
|
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown graphics API {}", graphics_api);
|
||||||
|
}
|
||||||
|
|
||||||
Core::System& system{Core::System::GetInstance()};
|
Core::System& system{Core::System::GetInstance()};
|
||||||
|
|
||||||
@ -174,9 +187,6 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
|||||||
system.RegisterMiiSelector(std::make_shared<MiiSelector::AndroidMiiSelector>());
|
system.RegisterMiiSelector(std::make_shared<MiiSelector::AndroidMiiSelector>());
|
||||||
system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>());
|
system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>());
|
||||||
|
|
||||||
// Register generic image interface
|
|
||||||
Core::System::GetInstance().RegisterImageInterface(std::make_shared<LodePNGImageInterface>());
|
|
||||||
|
|
||||||
// Register real Mic factory
|
// Register real Mic factory
|
||||||
Frontend::Mic::RegisterRealMicFactory(std::make_unique<Mic::AndroidFactory>());
|
Frontend::Mic::RegisterRealMicFactory(std::make_unique<Mic::AndroidFactory>());
|
||||||
|
|
||||||
@ -260,6 +270,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
|||||||
if (window) {
|
if (window) {
|
||||||
window->OnSurfaceChanged(s_surf);
|
window->OnSurfaceChanged(s_surf);
|
||||||
}
|
}
|
||||||
|
if (VideoCore::g_renderer) {
|
||||||
|
VideoCore::g_renderer->NotifySurfaceChanged();
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO(Frontend, "Surface changed");
|
LOG_INFO(Frontend, "Surface changed");
|
||||||
}
|
}
|
||||||
@ -318,6 +331,8 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetInstalledGamePaths(
|
|||||||
path += '/';
|
path += '/';
|
||||||
FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir);
|
FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir);
|
||||||
} else {
|
} else {
|
||||||
|
if (!FileUtil::Exists(path))
|
||||||
|
return false;
|
||||||
auto loader = Loader::GetLoader(path);
|
auto loader = Loader::GetLoader(path);
|
||||||
if (loader) {
|
if (loader) {
|
||||||
bool executable{};
|
bool executable{};
|
||||||
@ -491,6 +506,23 @@ void Java_org_citra_citra_1emu_NativeLibrary_CreateConfigFile(JNIEnv* env,
|
|||||||
Config{};
|
Config{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_NativeLibrary_CreateLogFile(JNIEnv* env,
|
||||||
|
[[maybe_unused]] jclass clazz) {
|
||||||
|
Log::RemoveBackend(Log::FileBackend::Name());
|
||||||
|
FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||||
|
Log::AddBackend(std::make_unique<Log::FileBackend>(
|
||||||
|
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + LOG_FILE));
|
||||||
|
LOG_INFO(Frontend, "Logging backend initialised");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_NativeLibrary_LogUserDirectory(JNIEnv* env,
|
||||||
|
[[maybe_unused]] jclass clazz,
|
||||||
|
jstring j_path) {
|
||||||
|
std::string_view path = env->GetStringUTFChars(j_path, 0);
|
||||||
|
LOG_INFO(Frontend, "User directory path: {}", path);
|
||||||
|
env->ReleaseStringUTFChars(j_path, path.data());
|
||||||
|
}
|
||||||
|
|
||||||
jint Java_org_citra_citra_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
jint Java_org_citra_citra_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
||||||
[[maybe_unused]] jclass clazz) {
|
[[maybe_unused]] jclass clazz) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -712,8 +744,7 @@ void Java_org_citra_citra_1emu_NativeLibrary_LoadState(JNIEnv* env, jclass clazz
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_citra_citra_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env, jclass clazz) {
|
void Java_org_citra_citra_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env, jclass clazz) {
|
||||||
// TODO: Log the Common::g_build_fullname once the CI is setup for android
|
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
|
||||||
LOG_INFO(Frontend, "Citra Version: Android Beta | {}-{}", Common::g_scm_branch,
|
|
||||||
Common::g_scm_desc);
|
Common::g_scm_desc);
|
||||||
LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
|
LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
|
||||||
// There is no decent way to get the OS version, so we log the API level instead.
|
// There is no decent way to get the OS version, so we log the API level instead.
|
||||||
|
@ -83,6 +83,13 @@ JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetSysDirectory(J
|
|||||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_CreateConfigFile(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_CreateConfigFile(JNIEnv* env,
|
||||||
jclass clazz);
|
jclass clazz);
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_CreateLogFile(JNIEnv* env,
|
||||||
|
jclass clazz);
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_LogUserDirectory(JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
jstring path);
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_citra_citra_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
JNIEXPORT jint JNICALL Java_org_citra_citra_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
||||||
jclass clazz);
|
jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetProfiling(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetProfiling(JNIEnv* env,
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<alpha
|
||||||
|
android:duration="125"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromAlpha="1"
|
||||||
|
android:toAlpha="0" />
|
||||||
|
|
||||||
|
<translate
|
||||||
|
android:duration="125"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromXDelta="0"
|
||||||
|
android:toXDelta="-75" />
|
||||||
|
|
||||||
|
</set>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<alpha
|
||||||
|
android:duration="@android:integer/config_shortAnimTime"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromAlpha="0"
|
||||||
|
android:toAlpha="1" />
|
||||||
|
|
||||||
|
<translate
|
||||||
|
android:duration="@android:integer/config_shortAnimTime"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromXDelta="-200"
|
||||||
|
android:toXDelta="0" />
|
||||||
|
|
||||||
|
</set>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<alpha
|
||||||
|
android:duration="125"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromAlpha="1"
|
||||||
|
android:toAlpha="0" />
|
||||||
|
|
||||||
|
<translate
|
||||||
|
android:duration="125"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromXDelta="0"
|
||||||
|
android:toXDelta="75" />
|
||||||
|
|
||||||
|
</set>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<alpha
|
||||||
|
android:duration="@android:integer/config_shortAnimTime"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromAlpha="0"
|
||||||
|
android:toAlpha="1" />
|
||||||
|
|
||||||
|
<translate
|
||||||
|
android:duration="@android:integer/config_shortAnimTime"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromXDelta="200"
|
||||||
|
android:toXDelta="0" />
|
||||||
|
|
||||||
|
</set>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<alpha
|
||||||
|
android:duration="@android:integer/config_shortAnimTime"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:fromAlpha="1"
|
||||||
|
android:toAlpha="0" />
|
||||||
|
|
||||||
|
</set>
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="yFraction"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="1.0"
|
|
||||||
android:valueTo="0" />
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="translationZ"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="100.0"
|
|
||||||
android:valueTo="0" />
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="elevation"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="100.0"
|
|
||||||
android:valueTo="0" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/accelerate_cubic"
|
|
||||||
android:propertyName="visibleness"
|
|
||||||
android:valueFrom="1.0f"
|
|
||||||
android:valueTo="0.6f"
|
|
||||||
android:valueType="floatType" />
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="translationZ"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="0"
|
|
||||||
android:valueTo="-100.0" />
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="elevation"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="0"
|
|
||||||
android:valueTo="-100.0" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="visibleness"
|
|
||||||
android:valueFrom="0.6f"
|
|
||||||
android:valueTo="1.0f"
|
|
||||||
android:valueType="floatType" />
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="translationZ"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="-100.0"
|
|
||||||
android:valueTo="0" />
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:duration="@android:integer/config_mediumAnimTime"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
|
||||||
android:propertyName="elevation"
|
|
||||||
android:startOffset="@android:integer/config_shortAnimTime"
|
|
||||||
android:valueFrom="-100.0"
|
|
||||||
android:valueTo="0" />
|
|
||||||
|
|
||||||
</set>
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user