Compare commits
219 Commits
blit-scree
...
vertex_spi
Author | SHA1 | Date | |
---|---|---|---|
5cdc08f6ea | |||
8779cb7785 | |||
a6ca7dca61 | |||
cce6a79a91 | |||
230a463a39 | |||
83e734bd6a | |||
72ee29669a | |||
b1a02e1710 | |||
2c34f41747 | |||
60d59730a9 | |||
850ec1f8b8 | |||
3da6c25fd8 | |||
0e987959a6 | |||
410b8b8809 | |||
d3392ae0b1 | |||
98e0ecf6a7 | |||
ad45b9880d | |||
0d1646e4df | |||
96f0746ab9 | |||
09dcd48257 | |||
62fc1f835e | |||
3b351c33d1 | |||
793485d201 | |||
3ef5ab7323 | |||
618c80c803 | |||
f7cb308243 | |||
b8583f9af3 | |||
33bf2b7c2d | |||
d48e6c04ce | |||
88f34a7d69 | |||
c8e9b465e2 | |||
278198f5f5 | |||
58718e6bd6 | |||
a814b21693 | |||
5478a4d634 | |||
8f87586495 | |||
7e3a0f524c | |||
922019cc22 | |||
41e9cdb645 | |||
e90add52d2 | |||
3c6ca2cc82 | |||
d1039d9a81 | |||
8b8cee1a5a | |||
5dc92cd72b | |||
d1503605a7 | |||
6426f7a319 | |||
3b9ed5234d | |||
763127605e | |||
b86b19d366 | |||
3dd74c69c5 | |||
01af8e3f2c | |||
474cccda33 | |||
6057b18172 | |||
6a4ff8fa24 | |||
3c79360fd3 | |||
939aafed40 | |||
23417787f8 | |||
8946c1a7de | |||
5fe910b18f | |||
3944cbdc19 | |||
8ac3dd1840 | |||
ff34287e4b | |||
81f2a0eaa1 | |||
f11715a4f4 | |||
89c51371f7 | |||
8076d893db | |||
72f8d520c9 | |||
f9274f8b9a | |||
3c09c03180 | |||
52251e3908 | |||
921444c2c9 | |||
89d234f642 | |||
13771b805b | |||
2a71059490 | |||
053221f155 | |||
4868c361e7 | |||
0c30dbf33e | |||
53370e81e2 | |||
b3fb260c84 | |||
599ca7caf7 | |||
abc0fd5e7b | |||
309b25d201 | |||
9ac7ef20b0 | |||
ebade3594d | |||
dca159d79f | |||
7007d5822a | |||
6f0fdf037f | |||
58621b0eb6 | |||
e8eef5c586 | |||
2b37997a95 | |||
558062efd7 | |||
5e880a4f26 | |||
238956f773 | |||
a5351bc596 | |||
5b6e99b194 | |||
3f3b4a2802 | |||
c19e8d36c9 | |||
2ec31f404b | |||
28a2805450 | |||
2601a1df6c | |||
96d5bb553b | |||
b4d0f442c8 | |||
2a68cab7d6 | |||
009d73fdf6 | |||
42af22f8fd | |||
b85d15b035 | |||
130e376c0c | |||
f884986257 | |||
e23dc3efb1 | |||
dd5e95d7c6 | |||
b72289eadd | |||
7a5d4f03da | |||
18fa277c71 | |||
73cc764091 | |||
9dea514d45 | |||
0bfaa035b9 | |||
85df778785 | |||
e54a92c252 | |||
9145d4cec8 | |||
c611592db6 | |||
6aba809da8 | |||
f0449d79fd | |||
33481ada7f | |||
67195974e7 | |||
70c2376fd0 | |||
177c7de4f9 | |||
eea914ba84 | |||
62e88fbeb3 | |||
5ce27d8341 | |||
eaf62eb635 | |||
9675811bbe | |||
945faf8e92 | |||
9403049671 | |||
40159d9779 | |||
f63653a5b9 | |||
c71dbb5d19 | |||
0f4df2c012 | |||
c6fc4f5a87 | |||
916afa194d | |||
6f2cd11a85 | |||
14652d52bc | |||
a57ee7cdf2 | |||
dbd3e6c29b | |||
665cbca17c | |||
efb9e9f40f | |||
8d35118f63 | |||
553c85456e | |||
68ca206d53 | |||
e30e977140 | |||
f13738d252 | |||
04b927ab7f | |||
993d172de9 | |||
695447611e | |||
06bacfbd72 | |||
cf8bc35d46 | |||
ef859bab84 | |||
a2d0669562 | |||
95365ad6ba | |||
1963b649e8 | |||
db7cdb741c | |||
0fe61ba040 | |||
b588d6181b | |||
c8ff1d744a | |||
ae3d50f71f | |||
ccb50e7f2c | |||
51e252c7ed | |||
517e0bc342 | |||
812c4fa059 | |||
cb82ffbe20 | |||
9d5ae8e1c2 | |||
06a9f69d88 | |||
84eada8c50 | |||
016ce6c286 | |||
48ee112ceb | |||
f261daf2fa | |||
0fc177e268 | |||
d1171328c9 | |||
850e5bf81c | |||
6da59d581e | |||
9c0fad21db | |||
0f250bed89 | |||
7cae35024f | |||
f298278f37 | |||
2238e6c3ef | |||
d09f6d97f6 | |||
1e6507d792 | |||
64062162c6 | |||
3b6ffd9c27 | |||
f44c95d638 | |||
4f715b6718 | |||
664562f988 | |||
2967068b87 | |||
d89a6d491e | |||
95c7bac8a6 | |||
81bf21283f | |||
de1fe7e6e3 | |||
35f7f5e3e3 | |||
6dabf10009 | |||
cb8b72069f | |||
a67f205cfe | |||
94d0399876 | |||
f6320c8de9 | |||
a76ef97f15 | |||
bb05d8c12a | |||
38b8bf12de | |||
d04d71e4c9 | |||
b9d9ae35e4 | |||
bd84dbc8d3 | |||
c7e259366d | |||
92ad651890 | |||
3201943423 | |||
7801907288 | |||
1ddea27ac8 | |||
aa84022704 | |||
14924e9db3 | |||
e14b9f7a25 | |||
fd7ada2a9c | |||
d396944487 | |||
c49379442d |
@ -17,7 +17,7 @@ then
|
||||
echo "Signing apk..."
|
||||
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > ks.jks
|
||||
|
||||
java -jar $(which apksigner) sign --ks ks.jks \
|
||||
apksigner sign --ks ks.jks \
|
||||
--ks-key-alias "${ANDROID_KEY_ALIAS}" \
|
||||
--ks-pass env:ANDROID_KEYSTORE_PASS "artifacts/${REV_NAME}.apk"
|
||||
fi
|
||||
|
@ -3,7 +3,7 @@
|
||||
brew update
|
||||
brew unlink python@2 || true
|
||||
rm '/usr/local/bin/2to3' || true
|
||||
brew install qt5 p7zip ccache ninja || true
|
||||
brew install qt5 molten-vk glslang vulkan-loader p7zip ccache ninja || true
|
||||
pip3 install macpack
|
||||
|
||||
export SDL_VER=2.0.16
|
||||
|
@ -12,20 +12,37 @@ cp build/bin/Release/citra "$REV_NAME"
|
||||
cp -r build/bin/Release/citra-qt.app "$REV_NAME"
|
||||
cp build/bin/Release/citra-room "$REV_NAME"
|
||||
|
||||
# move libs into folder for deployment
|
||||
macpack "${REV_NAME}/citra-qt.app/Contents/MacOS/citra-qt" -d "../Frameworks"
|
||||
# move qt frameworks into app bundle for deployment
|
||||
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app" -executable="${REV_NAME}/citra-qt.app/Contents/MacOS/citra-qt"
|
||||
BUNDLE_PATH="$REV_NAME/citra-qt.app"
|
||||
BUNDLE_CONTENTS_PATH="$BUNDLE_PATH/Contents"
|
||||
BUNDLE_EXECUTABLE_PATH="$BUNDLE_CONTENTS_PATH/MacOS/citra-qt"
|
||||
BUNDLE_LIB_PATH="$BUNDLE_CONTENTS_PATH/lib"
|
||||
BUNDLE_RESOURCES_PATH="$BUNDLE_CONTENTS_PATH/Resources"
|
||||
|
||||
CITRA_STANDALONE_PATH="$REV_NAME/citra"
|
||||
|
||||
# move libs into folder for deployment
|
||||
macpack "${REV_NAME}/citra" -d "libs"
|
||||
macpack $BUNDLE_EXECUTABLE_PATH -d "../Frameworks"
|
||||
# move qt frameworks into app bundle for deployment
|
||||
$(brew --prefix)/opt/qt5/bin/macdeployqt $BUNDLE_PATH -executable=$BUNDLE_EXECUTABLE_PATH
|
||||
|
||||
# move libs into folder for deployment
|
||||
macpack $CITRA_STANDALONE_PATH -d "libs"
|
||||
|
||||
# bundle MoltenVK
|
||||
VULKAN_LOADER_PATH=$(brew --prefix vulkan-loader)
|
||||
MOLTENVK_PATH=$(brew --prefix molten-vk)
|
||||
mkdir $BUNDLE_LIB_PATH
|
||||
cp $VULKAN_LOADER_PATH/lib/libvulkan.dylib $BUNDLE_LIB_PATH
|
||||
cp $MOLTENVK_PATH/lib/libMoltenVK.dylib $BUNDLE_LIB_PATH
|
||||
cp -r $MOLTENVK_PATH/share/vulkan $BUNDLE_RESOURCES_PATH
|
||||
install_name_tool -add_rpath "@loader_path/../lib/" $BUNDLE_EXECUTABLE_PATH
|
||||
|
||||
# workaround for libc++
|
||||
install_name_tool -change @loader_path/../Frameworks/libc++.1.0.dylib /usr/lib/libc++.1.dylib "${REV_NAME}/citra-qt.app/Contents/MacOS/citra-qt"
|
||||
install_name_tool -change @loader_path/libs/libc++.1.0.dylib /usr/lib/libc++.1.dylib "${REV_NAME}/citra"
|
||||
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/libs/libc++.1.0.dylib /usr/lib/libc++.1.dylib $CITRA_STANDALONE_PATH
|
||||
|
||||
# Make the launching script executable
|
||||
chmod +x ${REV_NAME}/citra-qt.app/Contents/MacOS/citra-qt
|
||||
chmod +x $BUNDLE_EXECUTABLE_PATH
|
||||
|
||||
# Verify loader instructions
|
||||
find "$REV_NAME" -type f -exec otool -L {} \;
|
||||
|
@ -1,7 +1,18 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MSVCCache.cmake" -DCITRA_USE_CCACHE=ON -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_MF=ON -DENABLE_FFMPEG_VIDEO_DUMPER=ON
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MSVCCache.cmake" \
|
||||
-DCITRA_USE_CCACHE=ON \
|
||||
-DCITRA_USE_BUNDLED_QT=1 \
|
||||
-DENABLE_QT_TRANSLATION=OFF \
|
||||
-DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DENABLE_MF=ON \
|
||||
-DENABLE_FFMPEG_VIDEO_DUMPER=ON
|
||||
|
||||
ninja
|
||||
# show the caching efficiency
|
||||
|
41
.ci/windows-msvc/upload.ps1
Normal file
41
.ci/windows-msvc/upload.ps1
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-", ""
|
||||
$GITREV = $(git show -s --format='%h')
|
||||
|
||||
# Find out what release we are building
|
||||
if ( $GIT_TAG_NAME ) {
|
||||
$RELEASE_NAME = ${GIT_TAG_NAME}.split("-")[0]
|
||||
$RELEASE_NAME = "${RELEASE_NAME}-msvc"
|
||||
}
|
||||
else {
|
||||
$RELEASE_NAME = "head"
|
||||
}
|
||||
|
||||
$MSVC_BUILD_ZIP = "citra-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
|
||||
$MSVC_SEVENZIP = "citra-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
|
||||
|
||||
$BUILD_DIR = ".\build\bin\Release"
|
||||
|
||||
# Create artifact directories
|
||||
mkdir $RELEASE_NAME
|
||||
mkdir "artifacts"
|
||||
|
||||
echo "Starting to pack ${RELEASE_NAME}"
|
||||
|
||||
Copy-Item $BUILD_DIR\* -Destination $RELEASE_NAME -Recurse
|
||||
Remove-Item $RELEASE_NAME\tests.* -ErrorAction ignore
|
||||
Remove-Item $RELEASE_NAME\*.pdb -ErrorAction ignore
|
||||
|
||||
# Copy documentation
|
||||
Copy-Item license.txt -Destination $RELEASE_NAME
|
||||
Copy-Item README.md -Destination $RELEASE_NAME
|
||||
|
||||
# Copy cross-platform scripting support
|
||||
Copy-Item dist\scripting -Destination $RELEASE_NAME -Recurse
|
||||
|
||||
# Build the final release artifacts
|
||||
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_NAME\*
|
||||
7z a $MSVC_SEVENZIP $RELEASE_NAME
|
||||
|
||||
Copy-Item $MSVC_BUILD_ZIP -Destination "artifacts"
|
||||
Copy-Item $MSVC_SEVENZIP -Destination "artifacts"
|
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
@ -95,6 +95,13 @@ jobs:
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.13"
|
||||
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
||||
- name: Pack
|
||||
run: ./.ci/macos/upload.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos
|
||||
path: artifacts/
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
@ -108,16 +115,34 @@ jobs:
|
||||
key: ${{ runner.os }}-win-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-win-
|
||||
- name: Query tag name
|
||||
uses: little-core-labs/get-git-tag@v3.0.2
|
||||
id: tagName
|
||||
- name: Install dependencies
|
||||
run: ./.ci/windows-msvc/deps.sh
|
||||
shell: bash
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-components: Glslang
|
||||
vulkan-use-cache: true
|
||||
- name: Test glslangValidator
|
||||
run: glslangValidator --version
|
||||
- name: Build
|
||||
run: ./.ci/windows-msvc/build.sh
|
||||
shell: bash
|
||||
env:
|
||||
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
||||
- name: Pack
|
||||
run: ./.ci/windows-msvc/upload.ps1
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc
|
||||
path: artifacts/
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -140,7 +165,7 @@ jobs:
|
||||
- name: Deps
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install ccache apksigner -y
|
||||
sudo apt-get install glslang-tools ccache apksigner -y
|
||||
- name: Build
|
||||
run: ./.ci/android/build.sh
|
||||
- name: Copy and sign artifacts
|
||||
@ -168,7 +193,7 @@ jobs:
|
||||
TRANSIFEX_API_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }}
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, android, macos, source]
|
||||
needs: [build, android, macos, source, windows]
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
|
12
.gitmodules
vendored
12
.gitmodules
vendored
@ -58,3 +58,15 @@
|
||||
[submodule "sdl2"]
|
||||
path = externals/sdl2/SDL
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
[submodule "vulkan-headers"]
|
||||
path = externals/vulkan-headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers
|
||||
[submodule "glslang"]
|
||||
path = externals/glslang
|
||||
url = https://github.com/KhronosGroup/glslang
|
||||
[submodule "glm"]
|
||||
path = externals/glm
|
||||
url = https://github.com/g-truc/glm
|
||||
[submodule "sirit"]
|
||||
path = externals/sirit
|
||||
url = https://github.com/GPUCode/sirit
|
||||
|
@ -1,15 +1,15 @@
|
||||
# CMake 3.12 required for 20 to be a valid value for CXX_STANDARD
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.15)
|
||||
# Don't override the warning flags in MSVC:
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
# Enforce new LTO setting
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
# Don't override the warning flags in MSVC:
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
# Enforce new LTO setting
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
include(DownloadExternals)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
project(citra LANGUAGES C CXX ASM)
|
||||
@ -43,6 +43,8 @@ CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_FFMPEG "Download bundled FFmpeg binarie
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
option(CITRA_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over FFmpeg)" ON "WIN32" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ON "MINGW" OFF)
|
||||
@ -51,6 +53,23 @@ option(USE_SYSTEM_BOOST "Use the system Boost libs (instead of the bundled ones)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_FDK "Use FDK AAC decoder" OFF "NOT ENABLE_FFMPEG_AUDIO_DECODER;NOT ENABLE_MF" OFF)
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
if (MSVC AND CCACHE)
|
||||
# buildcache does not properly cache PCH files, leading to compilation errors.
|
||||
# See https://github.com/mbitsnbites/buildcache/discussions/230
|
||||
message(WARNING "Buildcache does not properly support Precompiled Headers. Disabling PCH")
|
||||
set(CITRA_USE_PRECOMPILED_HEADERS OFF)
|
||||
endif()
|
||||
if(APPLE)
|
||||
message(WARNING "Precompiled Headers currently do not work on Apple. Disabling PCH")
|
||||
set(CITRA_USE_PRECOMPILED_HEADERS OFF)
|
||||
endif()
|
||||
endif()
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
message(STATUS "Using Precompiled Headers.")
|
||||
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
@ -158,6 +177,8 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
# Configure C++ standard
|
||||
# ===========================
|
||||
|
||||
# boost asio's concept usage doesn't play nicely with some compilers yet.
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
@ -174,8 +195,8 @@ find_package(Threads REQUIRED)
|
||||
|
||||
if (ENABLE_QT)
|
||||
if (CITRA_USE_BUNDLED_QT)
|
||||
if (MSVC_VERSION GREATER_EQUAL 1930 AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.15.5-msvc2022_64)
|
||||
if (MSVC_VERSION GREATER_EQUAL 1920 AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.15.7-msvc2019_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
|
||||
endif()
|
||||
@ -295,13 +316,15 @@ if (CLANG_FORMAT)
|
||||
set(SRCS ${PROJECT_SOURCE_DIR}/src)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h,*.mm -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}"
|
||||
COMMENT ${CCOMMENT})
|
||||
elseif(MINGW)
|
||||
if(MINGW)
|
||||
add_custom_target(clang-format
|
||||
COMMAND find `cygpath -u ${SRCS}` -iname *.h -o -iname *.cpp -o -iname *.mm | xargs `cygpath -u ${CLANG_FORMAT}` -i
|
||||
COMMENT ${CCOMMENT})
|
||||
else()
|
||||
add_custom_target(clang-format
|
||||
COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h,*.mm -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}"
|
||||
COMMENT ${CCOMMENT})
|
||||
endif()
|
||||
else()
|
||||
add_custom_target(clang-format
|
||||
COMMAND find ${SRCS} -iname *.h -o -iname *.cpp -o -iname *.mm | xargs ${CLANG_FORMAT} -i
|
||||
|
2
dist/qt_themes/colorful/style.qrc
vendored
2
dist/qt_themes/colorful/style.qrc
vendored
@ -13,6 +13,6 @@
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="colorful">
|
||||
<file>style.qss</file>
|
||||
<file alias="style.qss">../default/style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
4
dist/qt_themes/colorful/style.qss
vendored
4
dist/qt_themes/colorful/style.qss
vendored
@ -1,4 +0,0 @@
|
||||
/*
|
||||
This file is intentionally left blank.
|
||||
We do not want to apply any stylesheet for colorful, only icons.
|
||||
*/
|
17
dist/qt_themes/default/default.qrc
vendored
17
dist/qt_themes/default/default.qrc
vendored
@ -1,33 +1,22 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/default">
|
||||
<file alias="index.theme">icons/index.theme</file>
|
||||
|
||||
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
|
||||
|
||||
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
|
||||
|
||||
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
|
||||
|
||||
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
|
||||
|
||||
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
|
||||
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
|
||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||
|
||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||
|
||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||
|
||||
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
||||
|
||||
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
|
||||
|
||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||
|
||||
<file alias="256x256/citra.png">icons/256x256/citra.png</file>
|
||||
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="default">
|
||||
<file>style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
13
dist/qt_themes/default/style.qss
vendored
Normal file
13
dist/qt_themes/default/style.qss
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
QPushButton#GraphicsAPIStatusBarButton {
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
min-width: 60px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
21
dist/qt_themes/qdarkstyle/style.qss
vendored
21
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -522,13 +522,12 @@ QToolButton#qt_toolbar_ext_button {
|
||||
|
||||
QPushButton {
|
||||
color: #eff0f1;
|
||||
border-width: 1px;
|
||||
border-color: #54575B;
|
||||
border-style: solid;
|
||||
padding: 6px 4px;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
outline: none;
|
||||
min-width: 100px;
|
||||
min-height: 13px;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
@ -1237,3 +1236,17 @@ QPlainTextEdit:disabled {
|
||||
TouchScreenPreview {
|
||||
qproperty-dotHighlightColor: #3daee9;
|
||||
}
|
||||
|
||||
QPushButton#GraphicsAPIStatusBarButton {
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
min-width: 60px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
22
externals/CMakeLists.txt
vendored
22
externals/CMakeLists.txt
vendored
@ -60,6 +60,19 @@ endif()
|
||||
# Glad
|
||||
add_subdirectory(glad)
|
||||
|
||||
# glslang
|
||||
set(SKIP_GLSLANG_INSTALL ON)
|
||||
set(ENABLE_GLSLANG_BINARIES OFF)
|
||||
set(ENABLE_SPVREMAPPER OFF)
|
||||
set(ENABLE_CTEST OFF)
|
||||
add_subdirectory(glslang)
|
||||
|
||||
# Sirit
|
||||
add_subdirectory(sirit)
|
||||
|
||||
# glm
|
||||
add_subdirectory(glm)
|
||||
|
||||
# inih
|
||||
add_subdirectory(inih)
|
||||
|
||||
@ -154,3 +167,12 @@ if(ANDROID)
|
||||
add_subdirectory(libyuv)
|
||||
target_include_directories(yuv INTERFACE ./libyuv/include)
|
||||
endif()
|
||||
|
||||
# VMA
|
||||
add_library(vma INTERFACE)
|
||||
target_include_directories(vma INTERFACE ./vma)
|
||||
|
||||
# vulkan-headers
|
||||
add_library(vulkan-headers INTERFACE)
|
||||
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
|
||||
|
||||
|
@ -22,7 +22,7 @@ function(windows_copy_files TARGET SOURCE_DIR DEST_DIR)
|
||||
# cmake adds an extra check for command success which doesn't work too well with robocopy
|
||||
# so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
|
||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||
COMMAND if not exist ${DEST_DIR} mkdir ${DEST_DIR} 2> nul
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}
|
||||
COMMAND robocopy ${SOURCE_DIR} ${DEST_DIR} ${ARGN} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
|
||||
)
|
||||
endfunction()
|
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 3946dcf005...7a926d689b
1862
externals/glad/include/glad/glad.h
vendored
1862
externals/glad/include/glad/glad.h
vendored
File diff suppressed because it is too large
Load Diff
1037
externals/glad/src/glad.c
vendored
1037
externals/glad/src/glad.c
vendored
File diff suppressed because it is too large
Load Diff
1
externals/glm
vendored
Submodule
1
externals/glm
vendored
Submodule
Submodule externals/glm added at cc98465e35
1
externals/glslang
vendored
Submodule
1
externals/glslang
vendored
Submodule
Submodule externals/glslang added at c0cf8ad876
44
externals/microprofile/microprofile.h
vendored
44
externals/microprofile/microprofile.h
vendored
@ -834,7 +834,7 @@ struct MicroProfile
|
||||
|
||||
inline int MicroProfileLogType(MicroProfileLogEntry Index)
|
||||
{
|
||||
return ((MP_LOG_BEGIN_MASK & Index)>>62) & 0x3;
|
||||
return (int)(((MP_LOG_BEGIN_MASK & Index)>>62) & 0x3ULL);
|
||||
}
|
||||
|
||||
inline uint64_t MicroProfileLogTimerIndex(MicroProfileLogEntry Index)
|
||||
@ -847,7 +847,7 @@ inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfi
|
||||
MicroProfileLogEntry Entry = (nBegin<<62) | ((0x3fff&nToken)<<48) | (MP_LOG_TICK_MASK&nTick);
|
||||
int t = MicroProfileLogType(Entry);
|
||||
uint64_t nTimerIndex = MicroProfileLogTimerIndex(Entry);
|
||||
MP_ASSERT(t == nBegin);
|
||||
MP_ASSERT(static_cast<uint64_t>(t) == nBegin);
|
||||
MP_ASSERT(nTimerIndex == (nToken&0x3fff));
|
||||
return Entry;
|
||||
|
||||
@ -881,12 +881,12 @@ T MicroProfileMax(T a, T b)
|
||||
|
||||
inline int64_t MicroProfileMsToTick(float fMs, int64_t nTicksPerSecond)
|
||||
{
|
||||
return (int64_t)(fMs*0.001f*nTicksPerSecond);
|
||||
return (int64_t)(fMs*0.001f*(float)nTicksPerSecond);
|
||||
}
|
||||
|
||||
inline float MicroProfileTickToMsMultiplier(int64_t nTicksPerSecond)
|
||||
{
|
||||
return 1000.f / nTicksPerSecond;
|
||||
return 1000.f / (float)nTicksPerSecond;
|
||||
}
|
||||
|
||||
inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
|
||||
@ -902,8 +902,10 @@ inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
|
||||
#include <windows.h>
|
||||
#define snprintf _snprintf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#endif
|
||||
int64_t MicroProfileTicksPerSecondCpu()
|
||||
{
|
||||
static int64_t nTicksPerSecond = 0;
|
||||
@ -929,14 +931,14 @@ typedef void* (*MicroProfileThreadFunc)(void*);
|
||||
|
||||
#ifndef _WIN32
|
||||
typedef pthread_t MicroProfileThread;
|
||||
void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
||||
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
||||
{
|
||||
pthread_attr_t Attr;
|
||||
int r = pthread_attr_init(&Attr);
|
||||
MP_ASSERT(r == 0);
|
||||
pthread_create(pThread, &Attr, Func, 0);
|
||||
}
|
||||
void MicroProfileThreadJoin(MicroProfileThread* pThread)
|
||||
inline void MicroProfileThreadJoin(MicroProfileThread* pThread)
|
||||
{
|
||||
int r = pthread_join(*pThread, 0);
|
||||
MP_ASSERT(r == 0);
|
||||
@ -953,11 +955,11 @@ DWORD _stdcall ThreadTrampoline(void* pFunc)
|
||||
return static_cast<DWORD>(reinterpret_cast<uint64_t>(F(0)));
|
||||
}
|
||||
|
||||
void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
||||
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
||||
{
|
||||
*pThread = CreateThread(0, 0, ThreadTrampoline, Func, 0, 0);
|
||||
}
|
||||
void MicroProfileThreadJoin(MicroProfileThread* pThread)
|
||||
inline void MicroProfileThreadJoin(MicroProfileThread* pThread)
|
||||
{
|
||||
WaitForSingleObject(*pThread, INFINITE);
|
||||
CloseHandle(*pThread);
|
||||
@ -1154,7 +1156,7 @@ inline void MicroProfileSetThreadLog(MicroProfileThreadLog* pLog)
|
||||
pthread_setspecific(g_MicroProfileThreadLogKey, pLog);
|
||||
}
|
||||
#else
|
||||
MicroProfileThreadLog* MicroProfileGetThreadLog()
|
||||
inline MicroProfileThreadLog* MicroProfileGetThreadLog()
|
||||
{
|
||||
return g_MicroProfileThreadLog;
|
||||
}
|
||||
@ -1270,7 +1272,7 @@ MicroProfileToken MicroProfileFindToken(const char* pGroup, const char* pName)
|
||||
return MICROPROFILE_INVALID_TOKEN;
|
||||
}
|
||||
|
||||
uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type)
|
||||
inline uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type)
|
||||
{
|
||||
for(uint32_t i = 0; i < S.nGroupCount; ++i)
|
||||
{
|
||||
@ -1299,7 +1301,7 @@ uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type)
|
||||
return nGroupIndex;
|
||||
}
|
||||
|
||||
void MicroProfileRegisterGroup(const char* pGroup, const char* pCategory, uint32_t nColor)
|
||||
inline void MicroProfileRegisterGroup(const char* pGroup, const char* pCategory, uint32_t nColor)
|
||||
{
|
||||
int nCategoryIndex = -1;
|
||||
for(uint32_t i = 0; i < S.nCategoryCount; ++i)
|
||||
@ -1465,7 +1467,7 @@ void MicroProfileGpuLeave(MicroProfileToken nToken_, uint64_t nTickStart)
|
||||
}
|
||||
}
|
||||
|
||||
void MicroProfileContextSwitchPut(MicroProfileContextSwitch* pContextSwitch)
|
||||
inline void MicroProfileContextSwitchPut(MicroProfileContextSwitch* pContextSwitch)
|
||||
{
|
||||
if(S.nRunning || pContextSwitch->nTicks <= S.nPauseTicks)
|
||||
{
|
||||
@ -1579,10 +1581,10 @@ void MicroProfileFlip()
|
||||
|
||||
pFramePut->nFrameStartCpu = MP_TICK();
|
||||
pFramePut->nFrameStartGpu = (uint32_t)MicroProfileGpuInsertTimeStamp();
|
||||
if(pFrameNext->nFrameStartGpu != (uint64_t)-1)
|
||||
if(static_cast<uint64_t>(pFrameNext->nFrameStartGpu) != (uint64_t)-1)
|
||||
pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu);
|
||||
|
||||
if(pFrameCurrent->nFrameStartGpu == (uint64_t)-1)
|
||||
if(static_cast<uint64_t>(pFrameCurrent->nFrameStartGpu) == (uint64_t)-1)
|
||||
pFrameCurrent->nFrameStartGpu = pFrameNext->nFrameStartGpu + 1;
|
||||
|
||||
uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
|
||||
@ -1746,10 +1748,10 @@ void MicroProfileFlip()
|
||||
}
|
||||
}
|
||||
}
|
||||
for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
|
||||
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
|
||||
{
|
||||
pLog->nGroupTicks[i] += nGroupTicks[i];
|
||||
pFrameGroup[i] += nGroupTicks[i];
|
||||
pLog->nGroupTicks[j] += nGroupTicks[j];
|
||||
pFrameGroup[j] += nGroupTicks[j];
|
||||
}
|
||||
pLog->nStackPos = nStackPos;
|
||||
}
|
||||
@ -1917,7 +1919,7 @@ void MicroProfileSetEnableAllGroups(bool bEnableAllGroups)
|
||||
S.nAllGroupsWanted = bEnableAllGroups ? 1 : 0;
|
||||
}
|
||||
|
||||
void MicroProfileEnableCategory(const char* pCategory, bool bEnabled)
|
||||
inline void MicroProfileEnableCategory(const char* pCategory, bool bEnabled)
|
||||
{
|
||||
int nCategoryIndex = -1;
|
||||
for(uint32_t i = 0; i < S.nCategoryCount; ++i)
|
||||
@ -2027,7 +2029,7 @@ void MicroProfileForceDisableGroup(const char* pGroup, MicroProfileTokenType Typ
|
||||
}
|
||||
|
||||
|
||||
void MicroProfileCalcAllTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, float* pTotal, uint32_t nSize)
|
||||
inline void MicroProfileCalcAllTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, float* pTotal, uint32_t nSize)
|
||||
{
|
||||
for(uint32_t i = 0; i < S.nTotalTimers && i < nSize; ++i)
|
||||
{
|
||||
@ -3332,7 +3334,7 @@ bool MicroProfileIsLocalThread(uint32_t nThreadId)
|
||||
#endif
|
||||
#else
|
||||
|
||||
bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;}
|
||||
bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; }
|
||||
void MicroProfileStopContextSwitchTrace(){}
|
||||
void MicroProfileStartContextSwitchTrace(){}
|
||||
|
||||
@ -3580,7 +3582,7 @@ int MicroProfileGetGpuTickReference(int64_t* pOutCpu, int64_t* pOutGpu)
|
||||
|
||||
#undef S
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
12
externals/microprofile/microprofileui.h
vendored
12
externals/microprofile/microprofileui.h
vendored
@ -354,7 +354,7 @@ void MicroProfileInitUI()
|
||||
if(!bInitialized)
|
||||
{
|
||||
bInitialized = true;
|
||||
memset(&g_MicroProfileUI, 0, sizeof(g_MicroProfileUI));
|
||||
g_MicroProfileUI = {};
|
||||
UI.nActiveMenu = UINT32_MAX;
|
||||
UI.fDetailedOffsetTarget = UI.fDetailedOffset = 0.f;
|
||||
UI.fDetailedRangeTarget = UI.fDetailedRange = 50.f;
|
||||
@ -845,8 +845,8 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY,
|
||||
MicroProfile& S = *MicroProfileGet();
|
||||
MP_DEBUG_DUMP_RANGE();
|
||||
int nY = nBaseY - UI.nOffsetY;
|
||||
int64_t nNumBoxes = 0;
|
||||
int64_t nNumLines = 0;
|
||||
[[maybe_unused]] int64_t nNumBoxes = 0;
|
||||
[[maybe_unused]] int64_t nNumLines = 0;
|
||||
|
||||
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
|
||||
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
|
||||
@ -1988,7 +1988,7 @@ const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
|
||||
else
|
||||
{
|
||||
nIndex = nIndex-1;
|
||||
if(nIndex < UI.GroupMenuCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
|
||||
{
|
||||
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
|
||||
static char buffer[MICROPROFILE_NAME_MAX_LEN+32];
|
||||
@ -2135,7 +2135,7 @@ const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
|
||||
case 1: return "--";
|
||||
default:
|
||||
nIndex -= 2;
|
||||
if(nIndex < UI.nCustomCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.nCustomCount)
|
||||
{
|
||||
return UI.Custom[nIndex].pName;
|
||||
}
|
||||
@ -2185,7 +2185,7 @@ void MicroProfileUIClickGroups(int nIndex)
|
||||
else
|
||||
{
|
||||
nIndex -= 1;
|
||||
if(nIndex < UI.GroupMenuCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
|
||||
{
|
||||
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
|
||||
if(Item.nIsCategory)
|
||||
|
2
externals/sdl2/CMakeLists.txt
vendored
2
externals/sdl2/CMakeLists.txt
vendored
@ -39,9 +39,9 @@ set(SDL_JOYSTICK ON CACHE BOOL "")
|
||||
set(SDL_HAPTIC OFF CACHE BOOL "")
|
||||
set(SDL_HIDAPI ON CACHE BOOL "")
|
||||
set(SDL_POWER OFF CACHE BOOL "")
|
||||
set(SDL_THREADS ON CACHE BOOL "")
|
||||
set(SDL_TIMERS ON CACHE BOOL "")
|
||||
set(SDL_FILE ON CACHE BOOL "")
|
||||
set(SDL_THREADS ON CACHE BOOL "")
|
||||
set(SDL_LOADSO ON CACHE BOOL "")
|
||||
set(SDL_CPUINFO OFF CACHE BOOL "")
|
||||
set(SDL_FILESYSTEM OFF CACHE BOOL "")
|
||||
|
1
externals/sirit
vendored
Submodule
1
externals/sirit
vendored
Submodule
Submodule externals/sirit added at f0b6bbe55b
19670
externals/vma/vk_mem_alloc.h
vendored
Normal file
19670
externals/vma/vk_mem_alloc.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
externals/vulkan-headers
vendored
Submodule
1
externals/vulkan-headers
vendored
Submodule
Submodule externals/vulkan-headers added at 98f440ce68
@ -46,11 +46,21 @@ if (MSVC)
|
||||
|
||||
# Warnings
|
||||
/W3
|
||||
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4101 # 'identifier': unreferenced local variable
|
||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||
/we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4388 # signed/unsigned mismatch
|
||||
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||
)
|
||||
else()
|
||||
add_compile_options(
|
||||
/MP
|
||||
/Zo
|
||||
/permissive-
|
||||
/EHsc
|
||||
/volatile:iso
|
||||
@ -64,17 +74,26 @@ if (MSVC)
|
||||
|
||||
# Warnings
|
||||
/W3
|
||||
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4101 # 'identifier': unreferenced local variable
|
||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||
/we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4388 # signed/unsigned mismatch
|
||||
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||
)
|
||||
endif()
|
||||
|
||||
# Since MSVC's debugging information is not very deterministic, so we have to disable it
|
||||
# when using ccache or other caching tools
|
||||
if (NOT CITRA_USE_CCACHE)
|
||||
add_compile_options(
|
||||
/Zi
|
||||
/Zo
|
||||
)
|
||||
if (CITRA_USE_CCACHE OR CITRA_USE_PRECOMPILED_HEADERS)
|
||||
# Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21
|
||||
add_compile_options(/Z7)
|
||||
else()
|
||||
add_compile_options(/Zi)
|
||||
endif()
|
||||
|
||||
# /GS- - No stack buffer overflow checks
|
||||
@ -103,6 +122,7 @@ else()
|
||||
|
||||
if (MINGW)
|
||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||
add_compile_options("-Wa,-mbig-obj")
|
||||
if (COMPILE_WITH_DWARF)
|
||||
add_compile_options("-gdwarf")
|
||||
endif()
|
||||
|
@ -30,7 +30,8 @@
|
||||
android:supportsRtl="true"
|
||||
android:isGame="true"
|
||||
android:banner="@mipmap/ic_launcher"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:debuggable="true">
|
||||
|
||||
<activity
|
||||
android:name="org.citra.citra_emu.ui.main.MainActivity"
|
||||
|
@ -192,11 +192,15 @@ public final class SettingsFragmentPresenter {
|
||||
Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE);
|
||||
Setting systemClock = systemSection.getSetting(SettingsFile.KEY_INIT_CLOCK);
|
||||
Setting dateTime = systemSection.getSetting(SettingsFile.KEY_INIT_TIME);
|
||||
Setting pluginLoader = systemSection.getSetting(SettingsFile.KEY_PLUGIN_LOADER);
|
||||
Setting allowPluginLoader = systemSection.getSetting(SettingsFile.KEY_ALLOW_PLUGIN_LOADER);
|
||||
|
||||
sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_VALUE, Settings.SECTION_SYSTEM, R.string.emulated_region, 0, R.array.regionNames, R.array.regionValues, -1, region));
|
||||
sl.add(new SingleChoiceSetting(SettingsFile.KEY_LANGUAGE, Settings.SECTION_SYSTEM, R.string.emulated_language, 0, R.array.languageNames, R.array.languageValues, 1, language));
|
||||
sl.add(new SingleChoiceSetting(SettingsFile.KEY_INIT_CLOCK, Settings.SECTION_SYSTEM, R.string.init_clock, R.string.init_clock_description, R.array.systemClockNames, R.array.systemClockValues, 0, systemClock));
|
||||
sl.add(new DateTimeSetting(SettingsFile.KEY_INIT_TIME, Settings.SECTION_SYSTEM, R.string.init_time, R.string.init_time_description, "2000-01-01 00:00:01", dateTime));
|
||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_PLUGIN_LOADER, Settings.SECTION_SYSTEM, R.string.plugin_loader, R.string.plugin_loader_description, false, pluginLoader));
|
||||
sl.add(new CheckBoxSetting(SettingsFile.KEY_ALLOW_PLUGIN_LOADER, Settings.SECTION_SYSTEM, R.string.allow_plugin_loader, R.string.allow_plugin_loader_description, true, allowPluginLoader));
|
||||
}
|
||||
|
||||
private void addCameraSettings(ArrayList<SettingsItem> sl) {
|
||||
|
@ -78,6 +78,8 @@ public final class SettingsFile {
|
||||
public static final String KEY_IS_NEW_3DS = "is_new_3ds";
|
||||
public static final String KEY_REGION_VALUE = "region_value";
|
||||
public static final String KEY_LANGUAGE = "language";
|
||||
public static final String KEY_PLUGIN_LOADER = "plugin_loader";
|
||||
public static final String KEY_ALLOW_PLUGIN_LOADER = "allow_plugin_loader";
|
||||
|
||||
public static final String KEY_INIT_CLOCK = "init_clock";
|
||||
public static final String KEY_INIT_TIME = "init_time";
|
||||
|
@ -8,7 +8,7 @@ import org.citra.citra_emu.CitraApplication;
|
||||
public class EmulationMenuSettings {
|
||||
private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext());
|
||||
|
||||
// These must match what is defined in src/core/settings.h
|
||||
// These must match what is defined in src/common/settings.h
|
||||
public static final int LayoutOption_Default = 0;
|
||||
public static final int LayoutOption_SingleScreen = 1;
|
||||
public static final int LayoutOption_LargeScreen = 2;
|
||||
|
@ -19,6 +19,8 @@ add_library(citra-android SHARED
|
||||
default_ini.h
|
||||
emu_window/emu_window.cpp
|
||||
emu_window/emu_window.h
|
||||
emu_window/emu_window_vk.cpp
|
||||
emu_window/emu_window_vk.h
|
||||
game_info.cpp
|
||||
game_info.h
|
||||
game_settings.cpp
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "jni/camera/ndk_camera.h"
|
||||
@ -114,7 +114,13 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetString("Premium", "texture_filter_name", "none");
|
||||
|
||||
// Renderer
|
||||
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", true);
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(sdl2_config->GetInteger("Renderer", "graphics_api", 2));
|
||||
Settings::values.async_command_recording =
|
||||
sdl2_config->GetBoolean("Renderer", "async_command_recording", true);
|
||||
Settings::values.spirv_shader_gen =
|
||||
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_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true);
|
||||
Settings::values.shaders_accurate_mul =
|
||||
@ -139,9 +145,9 @@ void Config::ReadValues() {
|
||||
Settings::values.factor_3d =
|
||||
static_cast<u8>(sdl2_config->GetInteger("Renderer", "factor_3d", 0));
|
||||
std::string default_shader = "none (builtin)";
|
||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph)
|
||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph)
|
||||
default_shader = "dubois (builtin)";
|
||||
else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced)
|
||||
else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced)
|
||||
default_shader = "horizontal (builtin)";
|
||||
Settings::values.pp_shader_name =
|
||||
sdl2_config->GetString("Renderer", "pp_shader_name", default_shader);
|
||||
@ -186,9 +192,9 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetBoolean("Utility", "preload_textures", false);
|
||||
|
||||
// Audio
|
||||
Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false);
|
||||
Settings::values.enable_dsp_lle_multithread =
|
||||
sdl2_config->GetBoolean("Audio", "enable_dsp_lle_multithread", false);
|
||||
Settings::values.audio_emulation =
|
||||
static_cast<Settings::AudioEmulation>(sdl2_config->GetInteger(
|
||||
"Audio", "audio_emulation", static_cast<int>(Settings::AudioEmulation::HLE)));
|
||||
Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto");
|
||||
Settings::values.enable_audio_stretching =
|
||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
||||
@ -229,6 +235,10 @@ void Config::ReadValues() {
|
||||
std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
Settings::values.plugin_loader_enabled =
|
||||
sdl2_config->GetBoolean("System", "plugin_loader", false);
|
||||
Settings::values.allow_plugin_loader =
|
||||
sdl2_config->GetBoolean("System", "allow_plugin_loader", true);
|
||||
|
||||
// Camera
|
||||
using namespace Service::CAM;
|
||||
|
@ -281,6 +281,11 @@ init_clock =
|
||||
# Note: 3DS can only handle times later then Jan 1 2000
|
||||
init_time =
|
||||
|
||||
# Plugin loader state, if enabled plugins will be loaded from the SD card.
|
||||
# You can also set if homebrew apps are allowed to enable the plugin loader
|
||||
plugin_loader =
|
||||
allow_plugin_loader =
|
||||
|
||||
[Camera]
|
||||
# Which camera engine to use for the right outer camera
|
||||
# blank: a dummy camera that always returns black image
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "jni/id_cache.h"
|
||||
|
176
src/android/app/src/main/jni/emu_window/emu_window_vk.cpp
Normal file
176
src/android/app/src/main/jni/emu_window/emu_window_vk.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
// 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 "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "jni/emu_window/emu_window_vk.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 bool IsPortraitMode() {
|
||||
return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod(
|
||||
IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode());
|
||||
}
|
||||
|
||||
static void UpdateLandscapeScreenLayout() {
|
||||
Settings::values.layout_option =
|
||||
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();
|
||||
|
||||
if (core_context = CreateSharedContext(); !core_context) {
|
||||
LOG_CRITICAL(Frontend, "CreateSharedContext() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
bool EmuWindow_Android_Vulkan::CreateWindowSurface() {
|
||||
if (!host_window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
window_info.type = Frontend::WindowSystemType::Android;
|
||||
window_info.render_surface = host_window;
|
||||
|
||||
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 {
|
||||
return std::make_unique<SharedContext_Android>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void EmuWindow_Android_Vulkan::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_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();
|
||||
}
|
59
src/android/app/src/main/jni/emu_window/emu_window_vk.h
Normal file
59
src/android/app/src/main/jni/emu_window/emu_window_vk.h
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
struct ANativeWindow;
|
||||
|
||||
class SharedContext_Android : public Frontend::GraphicsContext {};
|
||||
|
||||
class EmuWindow_Android_Vulkan : public Frontend::EmuWindow {
|
||||
public:
|
||||
EmuWindow_Android_Vulkan(ANativeWindow* surface);
|
||||
~EmuWindow_Android_Vulkan();
|
||||
|
||||
void Present();
|
||||
|
||||
/// 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;
|
||||
|
||||
private:
|
||||
void OnFramebufferSizeChanged();
|
||||
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,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace GameSettings {
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
#include "jni/applets/mii_selector.h"
|
||||
#include "jni/applets/swkbd.h"
|
||||
#include "jni/camera/still_image_camera.h"
|
||||
@ -156,7 +156,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
|
||||
// Initialize Logger
|
||||
Log::Filter log_filter;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
Log::AddBackend(std::make_unique<Log::LogcatBackend>());
|
||||
FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
|
@ -17,23 +17,22 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/frontend/camera/factory.h"
|
||||
#include "core/frontend/mic.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/savestate.h"
|
||||
#include "core/settings.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/applets/mii_selector.h"
|
||||
#include "jni/applets/swkbd.h"
|
||||
#include "jni/camera/ndk_camera.h"
|
||||
#include "jni/camera/still_image_camera.h"
|
||||
#include "jni/config.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "jni/emu_window/emu_window_vk.h"
|
||||
#include "jni/game_info.h"
|
||||
#include "jni/game_settings.h"
|
||||
#include "jni/id_cache.h"
|
||||
@ -49,7 +48,7 @@ namespace {
|
||||
|
||||
ANativeWindow* s_surf;
|
||||
|
||||
std::unique_ptr<EmuWindow_Android> window;
|
||||
std::unique_ptr<EmuWindow_Android_Vulkan> window;
|
||||
|
||||
std::atomic<bool> stop_run{true};
|
||||
std::atomic<bool> pause_emulation{false};
|
||||
@ -147,7 +146,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
return Core::System::ResultStatus::ErrorLoader;
|
||||
}
|
||||
|
||||
window = std::make_unique<EmuWindow_Android>(s_surf);
|
||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf);
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
|
||||
@ -238,7 +237,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
}
|
||||
} else {
|
||||
// Ensure no audio bleeds out while game is paused
|
||||
const float volume = Settings::values.volume;
|
||||
const float volume = Settings::values.volume.GetValue();
|
||||
SCOPE_EXIT({ Settings::values.volume = volume; });
|
||||
Settings::values.volume = 0;
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
BIN
src/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so
Normal file
BIN
src/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so
Normal file
Binary file not shown.
@ -37,6 +37,10 @@
|
||||
<string name="init_time_description">Si el \"Tipo del reloj del sistema\" está en \"Reloj emulado\", ésto cambia la fecha y hora de inicio.</string>
|
||||
<string name="emulated_region">Región emulada</string>
|
||||
<string name="emulated_language">Idioma emulado</string>
|
||||
<string name="plugin_loader">Activar \"3GX Plugin Loader\"</string>
|
||||
<string name="plugin_loader_description">Carga \"3GX plugins\" de la SD emulada si están disponibles.</string>
|
||||
<string name="allow_plugin_loader">Permiter que apps cambien el estado del \"plugin loader\"</string>
|
||||
<string name="allow_plugin_loader_description">Permite a las aplicaciones homebrew activar el \"plugin loader\" incluso si está desactivado.</string>
|
||||
|
||||
<!-- Camera settings strings -->
|
||||
<string name="inner_camera">Cámara interior</string>
|
||||
|
@ -51,6 +51,10 @@
|
||||
<string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string>
|
||||
<string name="emulated_region">Emulated region</string>
|
||||
<string name="emulated_language">Emulated language</string>
|
||||
<string name="plugin_loader">Enable 3GX Plugin Loader</string>
|
||||
<string name="plugin_loader_description">Loads 3GX plugins from the emulated SD if they are available.</string>
|
||||
<string name="allow_plugin_loader">Allow apps to change plugin loader state</string>
|
||||
<string name="allow_plugin_loader_description">Allow homebrew apps to enable the plugin loader even when it is disabled.</string>
|
||||
|
||||
<!-- Camera settings strings -->
|
||||
<string name="inner_camera">Inner Camera</string>
|
||||
|
@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
@ -23,6 +23,7 @@ add_library(audio_core STATIC
|
||||
interpolate.cpp
|
||||
interpolate.h
|
||||
null_sink.h
|
||||
precompiled_headers.h
|
||||
sink.h
|
||||
sink_details.cpp
|
||||
sink_details.h
|
||||
@ -89,3 +90,7 @@ if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)
|
||||
endif()
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(audio_core PRIVATE precompiled_headers.h)
|
||||
endif()
|
||||
|
@ -7,17 +7,18 @@
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/dumping/backend.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
DspInterface::DspInterface() = default;
|
||||
DspInterface::~DspInterface() = default;
|
||||
|
||||
void DspInterface::SetSink(const std::string& sink_id, const std::string& audio_device) {
|
||||
sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
|
||||
void DspInterface::SetSink(std::string_view sink_id, std::string_view audio_device) {
|
||||
sink = CreateSinkFromID(Settings::values.sink_id.GetValue(),
|
||||
Settings::values.audio_device_id.GetValue());
|
||||
sink->SetCallback(
|
||||
[this](s16* buffer, std::size_t num_frames) { OutputCallback(buffer, num_frames); });
|
||||
time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
|
||||
@ -86,7 +87,7 @@ void DspInterface::OutputCallback(s16* buffer, std::size_t num_frames) {
|
||||
|
||||
// Implementation of the hardware volume slider
|
||||
// A cubic curve is used to approximate a linear change in human-perceived loudness
|
||||
const float linear_volume = std::clamp(Settings::values.volume, 0.0f, 1.0f);
|
||||
const float linear_volume = std::clamp(Settings::Volume(), 0.0f, 1.0f);
|
||||
if (linear_volume != 1.0) {
|
||||
const float volume_scale_factor = linear_volume * linear_volume * linear_volume;
|
||||
for (std::size_t i = 0; i < num_frames; i++) {
|
||||
|
@ -94,7 +94,7 @@ public:
|
||||
virtual void UnloadComponent() = 0;
|
||||
|
||||
/// Select the sink to use based on sink id.
|
||||
void SetSink(const std::string& sink_id, const std::string& audio_device);
|
||||
void SetSink(std::string_view sink_id, std::string_view audio_device);
|
||||
/// Get the current sink
|
||||
Sink& GetSink();
|
||||
/// Enable/Disable audio stretching.
|
||||
|
7
src/audio_core/precompiled_headers.h
Normal file
7
src/audio_core/precompiled_headers.h
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_precompiled_headers.h"
|
@ -10,6 +10,7 @@ add_executable(citra
|
||||
emu_window/emu_window_sdl2.h
|
||||
lodepng_image_interface.cpp
|
||||
lodepng_image_interface.h
|
||||
precompiled_headers.h
|
||||
resource.h
|
||||
)
|
||||
|
||||
@ -30,3 +31,7 @@ if (MSVC)
|
||||
include(CopyCitraSDLDeps)
|
||||
copy_citra_SDL_deps(citra)
|
||||
endif()
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(citra PRIVATE precompiled_headers.h)
|
||||
endif()
|
||||
|
@ -7,17 +7,7 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
|
||||
#include "common/microprofile.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#include "citra/config.h"
|
||||
#include "citra/emu_window/emu_window_sdl2.h"
|
||||
#include "citra/lodepng_image_interface.h"
|
||||
@ -25,23 +15,20 @@
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/dumping/backend.h"
|
||||
#include "core/file_sys/cia_container.h"
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
@ -52,6 +39,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
extern "C" {
|
||||
// tells Nvidia drivers to use the dedicated GPU by default on laptops with switchable graphics
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
@ -104,35 +96,35 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
|
||||
break;
|
||||
case Network::RoomMember::Error::CouldNotConnect:
|
||||
LOG_ERROR(Network, "Error: Could not connect");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::NameCollision:
|
||||
LOG_ERROR(
|
||||
Network,
|
||||
"You tried to use the same nickname as another user that is connected to the Room");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::MacCollision:
|
||||
LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is "
|
||||
"connected to the Room");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::ConsoleIdCollision:
|
||||
LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::WrongPassword:
|
||||
LOG_ERROR(Network, "Room replied with: Wrong password");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::WrongVersion:
|
||||
LOG_ERROR(Network,
|
||||
"You are using a different version than the room you are trying to connect to");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::RoomIsFull:
|
||||
LOG_ERROR(Network, "The room is full");
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
break;
|
||||
case Network::RoomMember::Error::HostKicked:
|
||||
LOG_ERROR(Network, "You have been kicked by the host");
|
||||
@ -140,6 +132,9 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
|
||||
case Network::RoomMember::Error::HostBanned:
|
||||
LOG_ERROR(Network, "You have been banned by the host");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Network, "Unknown network error: {}", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +167,7 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
|
||||
|
||||
static void InitializeLogging() {
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
|
||||
@ -190,8 +185,8 @@ int main(int argc, char** argv) {
|
||||
Common::DetachedTasks detached_tasks;
|
||||
Config config;
|
||||
int option_index = 0;
|
||||
bool use_gdbstub = Settings::values.use_gdbstub;
|
||||
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
|
||||
bool use_gdbstub = Settings::values.use_gdbstub.GetValue();
|
||||
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port.GetValue());
|
||||
std::string movie_record;
|
||||
std::string movie_record_author;
|
||||
std::string movie_play;
|
||||
@ -358,11 +353,23 @@ int main(int argc, char** argv) {
|
||||
// Register generic image interface
|
||||
Core::System::GetInstance().RegisterImageInterface(std::make_shared<LodePNGImageInterface>());
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)};
|
||||
Frontend::ScopeAcquireContext scope(*emu_window);
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
EmuWindow_SDL2::InitializeSDL2();
|
||||
|
||||
const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
|
||||
const auto emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen, false)};
|
||||
const bool use_secondary_window{Settings::values.layout_option.GetValue() ==
|
||||
Settings::LayoutOption::SeparateWindows};
|
||||
const auto secondary_window =
|
||||
use_secondary_window ? std::make_unique<EmuWindow_SDL2>(false, true) : nullptr;
|
||||
|
||||
const auto scope = emu_window->Acquire();
|
||||
|
||||
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
|
||||
Common::g_scm_desc);
|
||||
Settings::LogSettings();
|
||||
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
const Core::System::ResultStatus load_result{
|
||||
system.Load(*emu_window, filepath, secondary_window.get())};
|
||||
|
||||
switch (load_result) {
|
||||
case Core::System::ResultStatus::ErrorGetLoader:
|
||||
@ -430,7 +437,12 @@ int main(int argc, char** argv) {
|
||||
system.VideoDumper().StartDumping(dump_video, layout);
|
||||
}
|
||||
|
||||
std::thread render_thread([&emu_window] { emu_window->Present(); });
|
||||
std::thread main_render_thread([&emu_window] { emu_window->Present(); });
|
||||
std::thread secondary_render_thread([&secondary_window] {
|
||||
if (secondary_window) {
|
||||
secondary_window->Present();
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic_bool stop_run;
|
||||
system.Renderer().Rasterizer()->LoadDiskResources(
|
||||
@ -439,7 +451,11 @@ int main(int argc, char** argv) {
|
||||
total);
|
||||
});
|
||||
|
||||
while (emu_window->IsOpen()) {
|
||||
const auto secondary_is_open = [&secondary_window] {
|
||||
// if the secondary window isn't created, it shouldn't affect the main loop
|
||||
return secondary_window ? secondary_window->IsOpen() : true;
|
||||
};
|
||||
while (emu_window->IsOpen() && secondary_is_open()) {
|
||||
const auto result = system.RunLoop();
|
||||
|
||||
switch (result) {
|
||||
@ -453,13 +469,21 @@ int main(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
render_thread.join();
|
||||
emu_window->RequestClose();
|
||||
if (secondary_window) {
|
||||
secondary_window->RequestClose();
|
||||
}
|
||||
main_render_thread.join();
|
||||
secondary_render_thread.join();
|
||||
|
||||
Core::Movie::GetInstance().Shutdown();
|
||||
if (system.VideoDumper().IsDumping()) {
|
||||
system.VideoDumper().StopDumping();
|
||||
}
|
||||
|
||||
Network::Shutdown();
|
||||
InputCommon::Shutdown();
|
||||
|
||||
system.Shutdown();
|
||||
|
||||
detached_tasks.WaitForAllTasks();
|
||||
|
@ -13,9 +13,9 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/frontend/mic.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "network/network_settings.h"
|
||||
@ -109,7 +109,8 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100);
|
||||
|
||||
// Renderer
|
||||
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false);
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(sdl2_config->GetInteger("Renderer", "graphics_api", 0));
|
||||
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);
|
||||
#ifdef __APPLE__
|
||||
@ -128,24 +129,23 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", true);
|
||||
Settings::values.frame_limit =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
Settings::values.use_frame_limit_alternate =
|
||||
sdl2_config->GetBoolean("Renderer", "use_frame_limit_alternate", false);
|
||||
Settings::values.frame_limit_alternate =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit_alternate", 200));
|
||||
Settings::values.use_vsync_new =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync_new", 1));
|
||||
Settings::values.texture_filter_name =
|
||||
sdl2_config->GetString("Renderer", "texture_filter_name", "none");
|
||||
|
||||
Settings::values.mono_render_option = static_cast<Settings::MonoRenderOption>(
|
||||
sdl2_config->GetInteger("Renderer", "mono_render_option", 0));
|
||||
Settings::values.render_3d = static_cast<Settings::StereoRenderOption>(
|
||||
sdl2_config->GetInteger("Renderer", "render_3d", 0));
|
||||
Settings::values.factor_3d =
|
||||
static_cast<u8>(sdl2_config->GetInteger("Renderer", "factor_3d", 0));
|
||||
std::string default_shader = "none (builtin)";
|
||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph)
|
||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph)
|
||||
default_shader = "dubois (builtin)";
|
||||
else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
|
||||
Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced)
|
||||
else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced ||
|
||||
Settings::values.render_3d.GetValue() ==
|
||||
Settings::StereoRenderOption::ReverseInterlaced)
|
||||
default_shader = "horizontal (builtin)";
|
||||
Settings::values.pp_shader_name =
|
||||
sdl2_config->GetString("Renderer", "pp_shader_name", default_shader);
|
||||
@ -186,9 +186,8 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetBoolean("Utility", "preload_textures", false);
|
||||
|
||||
// Audio
|
||||
Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false);
|
||||
Settings::values.enable_dsp_lle_multithread =
|
||||
sdl2_config->GetBoolean("Audio", "enable_dsp_lle_multithread", false);
|
||||
Settings::values.audio_emulation = static_cast<Settings::AudioEmulation>(
|
||||
sdl2_config->GetInteger("Audio", "audio_emulation", 0));
|
||||
Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto");
|
||||
Settings::values.enable_audio_stretching =
|
||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
||||
@ -240,6 +239,58 @@ void Config::ReadValues() {
|
||||
.count();
|
||||
}
|
||||
|
||||
{
|
||||
constexpr const char* default_init_time_offset = "0 00:00:00";
|
||||
|
||||
std::string offset_string =
|
||||
sdl2_config->GetString("System", "init_time_offset", default_init_time_offset);
|
||||
|
||||
size_t sep_index = offset_string.find(' ');
|
||||
|
||||
if (sep_index == std::string::npos) {
|
||||
LOG_ERROR(Config, "Failed to parse init_time_offset. Using 0 00:00:00");
|
||||
offset_string = default_init_time_offset;
|
||||
|
||||
sep_index = offset_string.find(' ');
|
||||
}
|
||||
|
||||
std::string day_string = offset_string.substr(0, sep_index);
|
||||
long long days = 0;
|
||||
|
||||
try {
|
||||
days = std::stoll(day_string);
|
||||
} catch (std::logic_error&) {
|
||||
LOG_ERROR(Config, "Failed to parse days in init_time_offset. Using 0");
|
||||
days = 0;
|
||||
}
|
||||
|
||||
long long days_in_seconds = days * 86400;
|
||||
|
||||
std::tm t;
|
||||
t.tm_sec = 0;
|
||||
t.tm_min = 0;
|
||||
t.tm_hour = 0;
|
||||
t.tm_mday = 1;
|
||||
t.tm_mon = 0;
|
||||
t.tm_year = 100;
|
||||
t.tm_isdst = 0;
|
||||
|
||||
std::istringstream string_stream(offset_string.substr(sep_index + 1));
|
||||
string_stream >> std::get_time(&t, "%H:%M:%S");
|
||||
|
||||
if (string_stream.fail()) {
|
||||
LOG_ERROR(Config,
|
||||
"Failed to parse hours, minutes and seconds in init_time_offset. 00:00:00");
|
||||
}
|
||||
|
||||
auto time_offset =
|
||||
std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch();
|
||||
|
||||
auto secs = std::chrono::duration_cast<std::chrono::seconds>(time_offset).count();
|
||||
|
||||
Settings::values.init_time_offset = static_cast<long long>(secs) + days_in_seconds;
|
||||
}
|
||||
|
||||
// Camera
|
||||
using namespace Service::CAM;
|
||||
Settings::values.camera_name[OuterRightCamera] =
|
||||
|
@ -166,6 +166,10 @@ render_3d =
|
||||
# 0 - 100: Intensity. 0 (default)
|
||||
factor_3d =
|
||||
|
||||
# Change Default Eye to Render When in Monoscopic Mode
|
||||
# 0 (default): Left, 1: Right
|
||||
mono_render_option =
|
||||
|
||||
# The name of the post processing shader to apply.
|
||||
# Loaded from shaders if render_3d is off or side by side.
|
||||
# Loaded from shaders/anaglyph if render_3d is anaglyph
|
||||
@ -178,7 +182,11 @@ filter_mode =
|
||||
|
||||
[Layout]
|
||||
# Layout for the screen inside the render window.
|
||||
# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen, 3: Side by Side
|
||||
# 0 (default): Default Top Bottom Screen
|
||||
# 1: Single Screen Only
|
||||
# 2: Large Screen Small Screen
|
||||
# 3: Side by Side
|
||||
# 4: Separate Windows
|
||||
layout_option =
|
||||
|
||||
# Toggle custom layout (using the settings below) on or off.
|
||||
|
@ -12,9 +12,9 @@
|
||||
#include "citra/emu_window/emu_window_sdl2.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/3ds.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
@ -135,19 +135,11 @@ void EmuWindow_SDL2::Fullscreen() {
|
||||
SDL_MaximizeWindow(render_window);
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) {
|
||||
// Initialize the window
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}! Exiting...", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
InputCommon::Init();
|
||||
Network::Init();
|
||||
|
||||
SDL_SetMainReady();
|
||||
|
||||
if (Settings::values.use_gles) {
|
||||
const bool is_opengles =
|
||||
Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::OpenGLES;
|
||||
if (is_opengles) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
@ -201,7 +193,8 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader;
|
||||
render_window_id = SDL_GetWindowID(render_window);
|
||||
auto gl_load_func = is_opengles ? gladLoadGLES2Loader : gladLoadGLLoader;
|
||||
|
||||
if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError());
|
||||
@ -211,19 +204,26 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
|
||||
Common::g_scm_desc);
|
||||
Settings::LogSettings();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::~EmuWindow_SDL2() {
|
||||
core_context.reset();
|
||||
Network::Shutdown();
|
||||
InputCommon::Shutdown();
|
||||
SDL_GL_DeleteContext(window_context);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::InitializeSDL2() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}! Exiting...", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
InputCommon::Init();
|
||||
Network::Init();
|
||||
|
||||
SDL_SetMainReady();
|
||||
}
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
|
||||
return std::make_unique<SharedContext_SDL2>();
|
||||
}
|
||||
@ -240,7 +240,7 @@ void EmuWindow_SDL2::Present() {
|
||||
SDL_GL_MakeCurrent(render_window, window_context);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
while (IsOpen()) {
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
VideoCore::g_renderer->TryPresent(100, is_secondary);
|
||||
SDL_GL_SwapWindow(render_window);
|
||||
}
|
||||
SDL_GL_MakeCurrent(render_window, nullptr);
|
||||
@ -248,9 +248,14 @@ void EmuWindow_SDL2::Present() {
|
||||
|
||||
void EmuWindow_SDL2::PollEvents() {
|
||||
SDL_Event event;
|
||||
std::vector<SDL_Event> other_window_events;
|
||||
|
||||
// SDL_PollEvent returns 0 when there are no more events in the event queue
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.window.windowID != render_window_id) {
|
||||
other_window_events.push_back(event);
|
||||
continue;
|
||||
}
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
@ -299,16 +304,13 @@ void EmuWindow_SDL2::PollEvents() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
|
||||
const auto title =
|
||||
fmt::format("Citra {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
|
||||
results.emulation_speed * 100.0f);
|
||||
SDL_SetWindowTitle(render_window, title.c_str());
|
||||
last_time = current_time;
|
||||
for (auto& e : other_window_events) {
|
||||
// This is a somewhat hacky workaround to re-emit window events meant for another window
|
||||
// since SDL_PollEvent() is global but we poll events per window.
|
||||
SDL_PushEvent(&e);
|
||||
}
|
||||
if (!is_secondary) {
|
||||
UpdateFramerateCounter();
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,3 +325,16 @@ void EmuWindow_SDL2::DoneCurrent() {
|
||||
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::UpdateFramerateCounter() {
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
|
||||
const auto title =
|
||||
fmt::format("Citra {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
|
||||
results.emulation_speed * 100.0f);
|
||||
SDL_SetWindowTitle(render_window, title.c_str());
|
||||
last_time = current_time;
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,11 @@ private:
|
||||
|
||||
class EmuWindow_SDL2 : public Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(bool fullscreen);
|
||||
explicit EmuWindow_SDL2(bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
static void InitializeSDL2();
|
||||
|
||||
void Present();
|
||||
|
||||
/// Polls window events
|
||||
@ -88,12 +90,18 @@ private:
|
||||
/// Called when a configuration change affects the minimal size of the window
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
/// Called when polling to update framerate
|
||||
void UpdateFramerateCounter();
|
||||
|
||||
/// Is the window still open?
|
||||
bool is_open = true;
|
||||
|
||||
/// Internal SDL2 render window
|
||||
SDL_Window* render_window;
|
||||
|
||||
/// Internal SDL2 window ID
|
||||
int render_window_id{};
|
||||
|
||||
/// Fake hidden window for the core context
|
||||
SDL_Window* dummy_window;
|
||||
|
||||
|
7
src/citra/precompiled_headers.h
Normal file
7
src/citra/precompiled_headers.h
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_precompiled_headers.h"
|
@ -38,6 +38,8 @@ add_executable(citra-qt
|
||||
configuration/config.cpp
|
||||
configuration/config.h
|
||||
configuration/configure.ui
|
||||
configuration/configuration_shared.cpp
|
||||
configuration/configuration_shared.h
|
||||
configuration/configure_audio.cpp
|
||||
configuration/configure_audio.h
|
||||
configuration/configure_audio.ui
|
||||
@ -67,6 +69,9 @@ add_executable(citra-qt
|
||||
configuration/configure_motion_touch.cpp
|
||||
configuration/configure_motion_touch.h
|
||||
configuration/configure_motion_touch.ui
|
||||
configuration/configure_per_game.cpp
|
||||
configuration/configure_per_game.h
|
||||
configuration/configure_per_game.ui
|
||||
configuration/configure_storage.cpp
|
||||
configuration/configure_storage.h
|
||||
configuration/configure_storage.ui
|
||||
@ -159,6 +164,7 @@ add_executable(citra-qt
|
||||
multiplayer/state.cpp
|
||||
multiplayer/state.h
|
||||
multiplayer/validation.h
|
||||
precompiled_headers.h
|
||||
uisettings.cpp
|
||||
uisettings.h
|
||||
qt_image_interface.cpp
|
||||
@ -244,6 +250,8 @@ if (APPLE)
|
||||
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
||||
target_sources(citra-qt PRIVATE
|
||||
applesurfacehelper.h
|
||||
applesurfacehelper.mm
|
||||
macos_authorization.h
|
||||
macos_authorization.mm
|
||||
)
|
||||
@ -260,9 +268,13 @@ endif()
|
||||
create_target_directory_groups(citra-qt)
|
||||
|
||||
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia)
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad vma vulkan-headers nihstro-headers Qt5::Widgets Qt5::Multimedia)
|
||||
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
target_compile_definitions(citra-qt PRIVATE
|
||||
# Use QStringBuilder for string concatenation to reduce
|
||||
# the overall number of temporary strings created.
|
||||
@ -314,3 +326,7 @@ if (MSVC)
|
||||
copy_citra_FFmpeg_deps(citra-qt)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(citra-qt PRIVATE precompiled_headers.h)
|
||||
endif()
|
||||
|
11
src/citra_qt/applesurfacehelper.h
Normal file
11
src/citra_qt/applesurfacehelper.h
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace AppleSurfaceHelper {
|
||||
|
||||
void* GetSurfaceLayer(void* surface);
|
||||
|
||||
} // namespace AppleSurfaceHelper
|
16
src/citra_qt/applesurfacehelper.mm
Normal file
16
src/citra_qt/applesurfacehelper.mm
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "citra_qt/applesurfacehelper.h"
|
||||
|
||||
namespace AppleSurfaceHelper {
|
||||
|
||||
void* GetSurfaceLayer(void* surface) {
|
||||
NSView* view = static_cast<NSView*>(surface);
|
||||
return view.layer;
|
||||
}
|
||||
|
||||
} // AppleSurfaceHelper
|
@ -54,12 +54,12 @@ QtKeyboardDialog::QtKeyboardDialog(QWidget* parent, QtKeyboard* keyboard_)
|
||||
case ButtonConfig::None:
|
||||
break;
|
||||
}
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [=] { Submit(); });
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, [=] {
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [this] { Submit(); });
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, [this] {
|
||||
button = QtKeyboard::cancel_id;
|
||||
accept();
|
||||
});
|
||||
connect(buttons, &QDialogButtonBox::helpRequested, this, [=] {
|
||||
connect(buttons, &QDialogButtonBox::helpRequested, this, [this] {
|
||||
button = QtKeyboard::forgot_id;
|
||||
accept();
|
||||
});
|
||||
|
@ -6,67 +6,75 @@
|
||||
#include <QDragEnterEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLFunctions_4_3_Core>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/3ds.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "citra_qt/applesurfacehelper.h"
|
||||
#endif
|
||||
|
||||
#if !defined(WIN32)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
static GMainWindow* GetMainWindow() {
|
||||
for (QWidget* w : qApp->topLevelWidgets()) {
|
||||
const auto widgets = qApp->topLevelWidgets();
|
||||
for (QWidget* w : widgets) {
|
||||
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
|
||||
return main;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EmuThread::run() {
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
Frontend::ScopeAcquireContext scope(core_context);
|
||||
const auto scope = core_context.Acquire();
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources(
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
system.Renderer().Rasterizer()->LoadDiskResources(
|
||||
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
emit LoadProgress(stage, value, total);
|
||||
});
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
emit HideLoadingScreen();
|
||||
|
||||
core_context.MakeCurrent();
|
||||
|
||||
if (Core::System::GetInstance().frame_limiter.IsFrameAdvancing()) {
|
||||
if (system.frame_limiter.IsFrameAdvancing()) {
|
||||
// Usually the loading screen is hidden after the first frame is drawn. In this case
|
||||
// we hide it immediately as we need to wait for user input to start the emulation.
|
||||
emit HideLoadingScreen();
|
||||
Core::System::GetInstance().frame_limiter.WaitOnce();
|
||||
system.frame_limiter.WaitOnce();
|
||||
}
|
||||
|
||||
// Holds whether the cpu was running during the last iteration,
|
||||
// so that the DebugModeLeft signal can be emitted before the
|
||||
// next execution step.
|
||||
bool was_active = false;
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
while (!stop_run) {
|
||||
if (running) {
|
||||
if (!was_active)
|
||||
@ -111,91 +119,246 @@ void EmuThread::run() {
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
|
||||
: QWindow(parent), context(std::make_unique<QOpenGLContext>(shared_context->parent())),
|
||||
event_handler(event_handler) {
|
||||
class OpenGLSharedContext : public Frontend::GraphicsContext {
|
||||
public:
|
||||
/// Create the original context that should be shared from
|
||||
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
|
||||
QSurfaceFormat format;
|
||||
|
||||
format.setVersion(4, 4);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
|
||||
if (Settings::values.renderer_debug) {
|
||||
format.setOption(QSurfaceFormat::FormatOption::DebugContext);
|
||||
}
|
||||
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
format.setSwapInterval(0);
|
||||
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setFormat(format);
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create main openGL context");
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the shared contexts for rendering and presentation
|
||||
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0);
|
||||
this->setFormat(format);
|
||||
auto format = share_context->format();
|
||||
format.setSwapInterval(main_surface ? Settings::values.use_vsync_new.GetValue() : 0);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context->setScreen(this->screen());
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(share_context);
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create shared openGL context");
|
||||
}
|
||||
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
if (!main_surface) {
|
||||
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
|
||||
offscreen_surface->setFormat(format);
|
||||
offscreen_surface->create();
|
||||
surface = offscreen_surface.get();
|
||||
} else {
|
||||
surface = main_surface;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
}
|
||||
|
||||
OpenGLWindow::~OpenGLWindow() {
|
||||
~OpenGLSharedContext() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
void OpenGLWindow::Present() {
|
||||
if (!isExposed())
|
||||
return;
|
||||
|
||||
context->makeCurrent(this);
|
||||
if (VideoCore::g_renderer) {
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
}
|
||||
context->swapBuffers(this);
|
||||
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
|
||||
f->glFinish();
|
||||
QWindow::requestUpdate();
|
||||
}
|
||||
|
||||
bool OpenGLWindow::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::UpdateRequest:
|
||||
void SwapBuffers() override {
|
||||
context->swapBuffers(surface);
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
// We can't track the current state of the underlying context in this wrapper class because
|
||||
// Qt may make the underlying context not current for one reason or another. In particular,
|
||||
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
|
||||
// Instead of always just making the context current (which does not have any caching to
|
||||
// check if the underlying context is already current) we can check for the current context
|
||||
// in the thread local data by calling `currentContext()` and checking if its ours.
|
||||
if (QOpenGLContext::currentContext() != context.get()) {
|
||||
context->makeCurrent(surface);
|
||||
}
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
QOpenGLContext* GetShareContext() const {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
private:
|
||||
// Avoid using Qt parent system here since we might move the QObjects to new threads
|
||||
// As a note, this means we should avoid using slots/signals with the objects too
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
|
||||
QSurface* surface;
|
||||
};
|
||||
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
class RenderWidget : public QWidget {
|
||||
public:
|
||||
RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
}
|
||||
|
||||
virtual ~RenderWidget() = default;
|
||||
|
||||
virtual void Present() {}
|
||||
|
||||
void paintEvent(QPaintEvent* event) override {
|
||||
Present();
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::FocusIn:
|
||||
case QEvent::FocusOut:
|
||||
case QEvent::FocusAboutToChange:
|
||||
case QEvent::Enter:
|
||||
case QEvent::Leave:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::TabletMove:
|
||||
case QEvent::TabletPress:
|
||||
case QEvent::TabletRelease:
|
||||
case QEvent::TabletEnterProximity:
|
||||
case QEvent::TabletLeaveProximity:
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchEnd:
|
||||
case QEvent::InputMethodQuery:
|
||||
case QEvent::TouchCancel:
|
||||
return QCoreApplication::sendEvent(event_handler, event);
|
||||
case QEvent::Drop:
|
||||
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
case QEvent::DragEnter:
|
||||
case QEvent::DragMove:
|
||||
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
default:
|
||||
return QWindow::event(event);
|
||||
update();
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent* ev) override {
|
||||
render_window->resize(ev->size());
|
||||
render_window->OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override {
|
||||
InputCommon::GetKeyboard()->PressKey(event->key());
|
||||
}
|
||||
|
||||
void keyReleaseEvent(QKeyEvent* event) override {
|
||||
InputCommon::GetKeyboard()->ReleaseKey(event->key());
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchBeginEvent
|
||||
|
||||
const auto pos{event->pos()};
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
const auto [x, y] = render_window->ScaleTouch(pos);
|
||||
render_window->TouchPressed(x, y);
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||
}
|
||||
}
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchUpdateEvent
|
||||
|
||||
const auto pos{event->pos()};
|
||||
const auto [x, y] = render_window->ScaleTouch(pos);
|
||||
render_window->TouchMoved(x, y);
|
||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||
}
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent* event) override {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchEndEvent
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
render_window->TouchReleased();
|
||||
else if (event->button() == Qt::RightButton)
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GetSize() const {
|
||||
return std::make_pair(width(), height());
|
||||
}
|
||||
|
||||
QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
GRenderWindow* render_window;
|
||||
};
|
||||
|
||||
class OpenGLRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit OpenGLRenderWidget(GRenderWindow* parent, bool is_secondary)
|
||||
: RenderWidget(parent), is_secondary(is_secondary) {
|
||||
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
|
||||
}
|
||||
|
||||
void SetContext(std::unique_ptr<OpenGLSharedContext>&& context_) {
|
||||
context = std::move(context_);
|
||||
}
|
||||
|
||||
void Present() override {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
context->MakeCurrent();
|
||||
const auto f = context->GetShareContext()->extraFunctions();
|
||||
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
VideoCore::g_renderer->TryPresent(100, is_secondary);
|
||||
context->SwapBuffers();
|
||||
f->glFinish();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<OpenGLSharedContext> context{};
|
||||
bool is_secondary;
|
||||
};
|
||||
|
||||
class VulkanRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
|
||||
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||
}
|
||||
};
|
||||
|
||||
static Frontend::WindowSystemType GetWindowSystemType() {
|
||||
// Determine WSI type based on Qt platform.
|
||||
QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("windows"))
|
||||
return Frontend::WindowSystemType::Windows;
|
||||
else if (platform_name == QStringLiteral("xcb"))
|
||||
return Frontend::WindowSystemType::X11;
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
return Frontend::WindowSystemType::Wayland;
|
||||
else if (platform_name == QStringLiteral("cocoa"))
|
||||
return Frontend::WindowSystemType::MacOS;
|
||||
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
|
||||
return Frontend::WindowSystemType::Windows;
|
||||
}
|
||||
|
||||
void OpenGLWindow::exposeEvent(QExposeEvent* event) {
|
||||
QWindow::requestUpdate();
|
||||
QWindow::exposeEvent(event);
|
||||
static Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
|
||||
Frontend::EmuWindow::WindowSystemInfo wsi;
|
||||
wsi.type = GetWindowSystemType();
|
||||
|
||||
// Our Win32 Qt external doesn't have the private API.
|
||||
#if defined(WIN32)
|
||||
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
|
||||
#elif defined(__APPLE__)
|
||||
wsi.render_surface =
|
||||
window ? AppleSurfaceHelper::GetSurfaceLayer(reinterpret_cast<void*>(window->winId()))
|
||||
: nullptr;
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
wsi.display_connection = pni->nativeResourceForWindow("display", window);
|
||||
if (wsi.type == Frontend::WindowSystemType::Wayland)
|
||||
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
|
||||
else
|
||||
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
|
||||
#endif
|
||||
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
: QWidget(parent_), emu_thread(emu_thread) {
|
||||
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_)
|
||||
: QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) {
|
||||
|
||||
setWindowTitle(QStringLiteral("Citra %1 | %2-%3")
|
||||
.arg(QString::fromUtf8(Common::g_build_name),
|
||||
@ -205,7 +368,6 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
auto layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
InputCommon::Init();
|
||||
|
||||
this->setMouseTracking(true);
|
||||
|
||||
@ -213,16 +375,14 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
InputCommon::Shutdown();
|
||||
}
|
||||
GRenderWindow::~GRenderWindow() = default;
|
||||
|
||||
void GRenderWindow::MakeCurrent() {
|
||||
core_context->MakeCurrent();
|
||||
main_context->MakeCurrent();
|
||||
}
|
||||
|
||||
void GRenderWindow::DoneCurrent() {
|
||||
core_context->DoneCurrent();
|
||||
main_context->DoneCurrent();
|
||||
}
|
||||
|
||||
void GRenderWindow::PollEvents() {
|
||||
@ -380,6 +540,12 @@ bool GRenderWindow::event(QEvent* event) {
|
||||
void GRenderWindow::focusOutEvent(QFocusEvent* event) {
|
||||
QWidget::focusOutEvent(event);
|
||||
InputCommon::GetKeyboard()->ReleaseAllKeys();
|
||||
has_focus = false;
|
||||
}
|
||||
|
||||
void GRenderWindow::focusInEvent(QFocusEvent* event) {
|
||||
QWidget::focusInEvent(event);
|
||||
has_focus = true;
|
||||
}
|
||||
|
||||
void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||
@ -387,43 +553,80 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||
OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api.GetValue();
|
||||
if (graphics_api == Settings::GraphicsAPI::OpenGL ||
|
||||
graphics_api == Settings::GraphicsAPI::OpenGLES) {
|
||||
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
|
||||
// Bind the shared contexts to the main surface in case the backend wants to take over
|
||||
// presentation
|
||||
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
|
||||
child_widget->windowHandle());
|
||||
}
|
||||
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitRenderTarget() {
|
||||
ReleaseRenderTarget();
|
||||
|
||||
{
|
||||
// Create a dummy render widget so that Qt
|
||||
// places the render window at the correct position.
|
||||
const RenderWidget dummy_widget{this};
|
||||
}
|
||||
|
||||
first_frame = false;
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
|
||||
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
|
||||
child_window->create();
|
||||
child_widget = createWindowContainer(child_window, this);
|
||||
const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api.GetValue();
|
||||
switch (graphics_api) {
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
case Settings::GraphicsAPI::OpenGLES:
|
||||
if (!InitializeOpenGL()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
if (!InitializeVulkan()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the Window System information with the new render target
|
||||
window_info = GetWindowSystemInfo(child_widget->windowHandle());
|
||||
|
||||
child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
|
||||
|
||||
layout()->addWidget(child_widget);
|
||||
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
||||
setMinimumSize(1, 1);
|
||||
|
||||
core_context = CreateSharedContext();
|
||||
resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
OnFramebufferSizeChanged();
|
||||
BackupGeometry();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GRenderWindow::ReleaseRenderTarget() {
|
||||
if (child_widget) {
|
||||
layout()->removeWidget(child_widget);
|
||||
delete child_widget;
|
||||
child_widget->deleteLater();
|
||||
child_widget = nullptr;
|
||||
}
|
||||
main_context.reset();
|
||||
}
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||
if (res_scale == 0)
|
||||
res_scale = VideoCore::GetResolutionScaleFactor();
|
||||
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
|
||||
const auto layout{Layout::FrameLayoutFromResolutionScale(res_scale, is_secondary)};
|
||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||
VideoCore::RequestScreenshot(
|
||||
screenshot_image.bits(),
|
||||
[=] {
|
||||
[this, screenshot_path] {
|
||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
|
||||
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
||||
@ -438,6 +641,29 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeOpenGL() {
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
auto child = new OpenGLRenderWidget(this, is_secondary);
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
|
||||
main_context = context;
|
||||
child->SetContext(
|
||||
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeVulkan() {
|
||||
auto child = new VulkanRenderWidget(this);
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
main_context = std::make_unique<DummyContext>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
this->emu_thread = emu_thread;
|
||||
}
|
||||
@ -449,31 +675,3 @@ void GRenderWindow::OnEmulationStopping() {
|
||||
void GRenderWindow::showEvent(QShowEvent* event) {
|
||||
QWidget::showEvent(event);
|
||||
}
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
return std::make_unique<GLContext>(QOpenGLContext::globalShareContext());
|
||||
}
|
||||
|
||||
GLContext::GLContext(QOpenGLContext* shared_context)
|
||||
: context(std::make_unique<QOpenGLContext>(shared_context->parent())),
|
||||
surface(std::make_unique<QOffscreenSurface>(nullptr)) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
format.setSwapInterval(0);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
surface->setParent(shared_context->parent());
|
||||
surface->setFormat(format);
|
||||
surface->create();
|
||||
}
|
||||
|
||||
void GLContext::MakeCurrent() {
|
||||
context->makeCurrent(surface.get());
|
||||
}
|
||||
|
||||
void GLContext::DoneCurrent() {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
@ -27,19 +27,6 @@ namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
}
|
||||
|
||||
class GLContext : public Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GLContext(QOpenGLContext* shared_context);
|
||||
|
||||
void MakeCurrent() override;
|
||||
|
||||
void DoneCurrent() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
std::unique_ptr<QOffscreenSurface> surface;
|
||||
};
|
||||
|
||||
class EmuThread final : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
@ -126,29 +113,11 @@ signals:
|
||||
void HideLoadingScreen();
|
||||
};
|
||||
|
||||
class OpenGLWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context);
|
||||
|
||||
~OpenGLWindow();
|
||||
|
||||
void Present();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
void exposeEvent(QExposeEvent* event) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
QWidget* event_handler;
|
||||
};
|
||||
|
||||
class GRenderWindow : public QWidget, public Frontend::EmuWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
|
||||
GRenderWindow(QWidget* parent, EmuThread* emu_thread, bool is_secondary);
|
||||
~GRenderWindow() override;
|
||||
|
||||
// EmuWindow implementation.
|
||||
@ -178,14 +147,20 @@ public:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
void focusInEvent(QFocusEvent* event) override;
|
||||
bool HasFocus() const {
|
||||
return has_focus;
|
||||
}
|
||||
|
||||
void InitRenderTarget();
|
||||
bool InitRenderTarget();
|
||||
|
||||
/// Destroy the previous run's child_widget which should also destroy the child_window
|
||||
void ReleaseRenderTarget();
|
||||
|
||||
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
||||
|
||||
std::pair<u32, u32> ScaleTouch(const QPointF pos) const;
|
||||
|
||||
public slots:
|
||||
|
||||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
@ -205,30 +180,31 @@ signals:
|
||||
void MouseActivity();
|
||||
|
||||
private:
|
||||
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
|
||||
void TouchBeginEvent(const QTouchEvent* event);
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
std::unique_ptr<GraphicsContext> core_context;
|
||||
|
||||
QByteArray geometry;
|
||||
|
||||
/// Native window handle that backs this presentation widget
|
||||
QWindow* child_window = nullptr;
|
||||
|
||||
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
|
||||
/// put the child_window into a widget then add it to the layout. This child_widget can be
|
||||
/// parented to GRenderWindow and use Qt's lifetime system
|
||||
QWidget* child_widget = nullptr;
|
||||
bool InitializeOpenGL();
|
||||
bool InitializeVulkan();
|
||||
|
||||
EmuThread* emu_thread;
|
||||
|
||||
// Main context that will be shared with all other contexts that are requested.
|
||||
// If this is used in a shared context setting, then this should not be used directly, but
|
||||
// should instead be shared from
|
||||
std::shared_ptr<Frontend::GraphicsContext> main_context;
|
||||
|
||||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
|
||||
QByteArray geometry;
|
||||
|
||||
QWidget* child_widget = nullptr;
|
||||
|
||||
bool first_frame = false;
|
||||
bool has_focus = false;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
@ -19,25 +19,31 @@ namespace Camera {
|
||||
QList<QVideoFrame::PixelFormat> QtCameraSurface::supportedPixelFormats(
|
||||
[[maybe_unused]] QAbstractVideoBuffer::HandleType handleType) const {
|
||||
return QList<QVideoFrame::PixelFormat>()
|
||||
<< QVideoFrame::Format_ARGB32 << QVideoFrame::Format_ARGB32_Premultiplied
|
||||
<< QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24 << QVideoFrame::Format_RGB565
|
||||
<< QVideoFrame::Format_RGB555 << QVideoFrame::Format_ARGB8565_Premultiplied
|
||||
<< QVideoFrame::Format_BGRA32 << QVideoFrame::Format_BGRA32_Premultiplied
|
||||
<< QVideoFrame::Format_BGR32 << QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565
|
||||
<< QVideoFrame::Format_BGR555 << QVideoFrame::Format_BGRA5658_Premultiplied
|
||||
<< QVideoFrame::Format_AYUV444 << QVideoFrame::Format_AYUV444_Premultiplied
|
||||
<< QVideoFrame::Format_YUV444 << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12
|
||||
<< QVideoFrame::Format_UYVY << QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12
|
||||
<< QVideoFrame::Format_NV21 << QVideoFrame::Format_IMC1 << QVideoFrame::Format_IMC2
|
||||
<< QVideoFrame::Format_IMC3 << QVideoFrame::Format_IMC4 << QVideoFrame::Format_Y8
|
||||
<< QVideoFrame::Format_Y16 << QVideoFrame::Format_Jpeg << QVideoFrame::Format_CameraRaw
|
||||
<< QVideoFrame::Format_AdobeDng; // Supporting all the formats
|
||||
<< QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24
|
||||
<< QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_ARGB32
|
||||
<< QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555
|
||||
<< QVideoFrame::Format_Jpeg
|
||||
// the following formats are supported via Qt internal conversions
|
||||
<< QVideoFrame::Format_ARGB8565_Premultiplied << QVideoFrame::Format_BGRA32
|
||||
<< QVideoFrame::Format_BGRA32_Premultiplied << QVideoFrame::Format_BGR32
|
||||
<< QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565 << QVideoFrame::Format_BGR555
|
||||
<< QVideoFrame::Format_AYUV444 << QVideoFrame::Format_YUV444
|
||||
<< QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_UYVY
|
||||
<< QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12
|
||||
<< QVideoFrame::Format_NV21; // Supporting all the QImage convertible formats, ordered by
|
||||
// QImage decoding performance
|
||||
}
|
||||
|
||||
bool QtCameraSurface::present(const QVideoFrame& frame) {
|
||||
if (!frame.isValid()) {
|
||||
return false;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
QMutexLocker locker(&mutex);
|
||||
// In Qt 5.15, the image is already flipped
|
||||
current_frame = frame.image();
|
||||
locker.unlock();
|
||||
#else
|
||||
QVideoFrame cloneFrame(frame);
|
||||
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
|
||||
const QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(),
|
||||
@ -46,6 +52,7 @@ bool QtCameraSurface::present(const QVideoFrame& frame) {
|
||||
current_frame = image.mirrored(true, true);
|
||||
locker.unlock();
|
||||
cloneFrame.unmap();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -199,6 +206,7 @@ void QtMultimediaCameraHandler::StartCamera() {
|
||||
camera->setViewfinderSettings(settings);
|
||||
camera->start();
|
||||
started = true;
|
||||
paused = false;
|
||||
}
|
||||
|
||||
bool QtMultimediaCameraHandler::CameraAvailable() const {
|
||||
@ -210,13 +218,14 @@ void QtMultimediaCameraHandler::StopCameras() {
|
||||
for (auto& handler : handlers) {
|
||||
if (handler && handler->started) {
|
||||
handler->StopCamera();
|
||||
handler->paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtMultimediaCameraHandler::ResumeCameras() {
|
||||
for (auto& handler : handlers) {
|
||||
if (handler && handler->started) {
|
||||
if (handler && handler->paused) {
|
||||
handler->StartCamera();
|
||||
}
|
||||
}
|
||||
@ -228,6 +237,7 @@ void QtMultimediaCameraHandler::ReleaseHandlers() {
|
||||
for (std::size_t i = 0; i < handlers.size(); i++) {
|
||||
status[i] = false;
|
||||
handlers[i]->started = false;
|
||||
handlers[i]->paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ private:
|
||||
QtCameraSurface camera_surface{};
|
||||
QCameraViewfinderSettings settings;
|
||||
bool started = false;
|
||||
bool paused = false; // was previously started but was paused, to be resumed
|
||||
|
||||
static std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> handlers;
|
||||
static std::array<bool, 3> status;
|
||||
|
@ -37,7 +37,7 @@ CheatDialog::CheatDialog(QWidget* parent)
|
||||
connect(ui->textNotes, &QPlainTextEdit::textChanged, this, &CheatDialog::OnTextEdited);
|
||||
connect(ui->textCode, &QPlainTextEdit::textChanged, this, &CheatDialog::OnTextEdited);
|
||||
|
||||
connect(ui->buttonSave, &QPushButton::clicked,
|
||||
connect(ui->buttonSave, &QPushButton::clicked, this,
|
||||
[this] { SaveCheat(ui->tableCheats->currentRow()); });
|
||||
connect(ui->buttonDelete, &QPushButton::clicked, this, &CheatDialog::OnDeleteCheat);
|
||||
|
||||
@ -91,7 +91,7 @@ bool CheatDialog::SaveCheat(int row) {
|
||||
}
|
||||
|
||||
// Check if the cheat lines are valid
|
||||
auto code_lines = ui->textCode->toPlainText().split(QLatin1Char{'\n'}, QString::SkipEmptyParts);
|
||||
auto code_lines = ui->textCode->toPlainText().split(QLatin1Char{'\n'}, Qt::SkipEmptyParts);
|
||||
for (int i = 0; i < code_lines.size(); ++i) {
|
||||
Cheats::GatewayCheat::CheatLine cheat_line(code_lines[i].toStdString());
|
||||
if (cheat_line.valid)
|
||||
@ -190,8 +190,9 @@ void CheatDialog::OnDeleteCheat() {
|
||||
if (newly_created) {
|
||||
newly_created = false;
|
||||
} else {
|
||||
Core::System::GetInstance().CheatEngine().RemoveCheat(ui->tableCheats->currentRow());
|
||||
Core::System::GetInstance().CheatEngine().SaveCheatFile();
|
||||
auto& cheat_engine = Core::System::GetInstance().CheatEngine();
|
||||
cheat_engine.RemoveCheat(ui->tableCheats->currentRow());
|
||||
cheat_engine.SaveCheatFile();
|
||||
}
|
||||
|
||||
LoadCheats();
|
||||
|
@ -17,17 +17,15 @@
|
||||
#include "network/network.h"
|
||||
#include "network/network_settings.h"
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config =
|
||||
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
Reload();
|
||||
Config::Config(const std::string& config_name, ConfigType config_type) : type{config_type} {
|
||||
global = config_type == ConfigType::GlobalConfig;
|
||||
Initialize(config_name);
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
if (global) {
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
|
||||
@ -58,7 +56,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 23> default_hotkeys{
|
||||
const std::array<UISettings::Shortcut, 24> default_hotkeys{
|
||||
{{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
@ -70,6 +68,7 @@ const std::array<UISettings::Shortcut, 23> default_hotkeys{
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
||||
@ -84,46 +83,176 @@ const std::array<UISettings::Shortcut, 23> default_hotkeys{
|
||||
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+D"), Qt::ApplicationShortcut}}}};
|
||||
// clang-format on
|
||||
|
||||
void Config::Initialize(const std::string& config_name) {
|
||||
const std::string fs_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir);
|
||||
const std::string config_file = fmt::format("{}.ini", config_name);
|
||||
|
||||
switch (type) {
|
||||
case ConfigType::GlobalConfig:
|
||||
qt_config_loc = fmt::format("{}/{}", fs_config_loc, config_file);
|
||||
break;
|
||||
case ConfigType::PerGameConfig:
|
||||
qt_config_loc = fmt::format("{}/custom/{}", fs_config_loc, config_file);
|
||||
break;
|
||||
}
|
||||
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config =
|
||||
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
Reload();
|
||||
}
|
||||
|
||||
/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their
|
||||
* usages later in this file. This allows explicit definition of some types that don't work
|
||||
* nicely with the general version.
|
||||
*/
|
||||
|
||||
// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor
|
||||
// can it implicitly convert a QVariant back to a {std::,Q}string
|
||||
template <>
|
||||
void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const auto default_value = QString::fromStdString(setting.GetDefault());
|
||||
if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
|
||||
setting.SetValue(default_value.toStdString());
|
||||
} else {
|
||||
setting.SetValue(qt_config->value(name, default_value).toString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void Config::ReadBasicSetting(Settings::Setting<Type, ranged>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const Type default_value = setting.GetDefault();
|
||||
if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
|
||||
setting.SetValue(default_value);
|
||||
} else {
|
||||
QVariant value{};
|
||||
if constexpr (std::is_enum_v<Type>) {
|
||||
using TypeU = std::underlying_type_t<Type>;
|
||||
value = qt_config->value(name, static_cast<TypeU>(default_value));
|
||||
setting.SetValue(static_cast<Type>(value.value<TypeU>()));
|
||||
} else {
|
||||
value = qt_config->value(name, QVariant::fromValue(default_value));
|
||||
setting.SetValue(value.value<Type>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting) {
|
||||
QString name = QString::fromStdString(setting.GetLabel());
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
setting.SetGlobal(use_global);
|
||||
if (global || !use_global) {
|
||||
QVariant value{};
|
||||
if constexpr (std::is_enum_v<Type>) {
|
||||
using TypeU = std::underlying_type_t<Type>;
|
||||
value = QVariant::fromValue<TypeU>(static_cast<TypeU>(setting.GetDefault()));
|
||||
setting.SetValue(static_cast<Type>(ReadSetting(name, value).value<TypeU>()));
|
||||
} else {
|
||||
value = QVariant::fromValue<Type>(setting.GetDefault());
|
||||
setting.SetValue(ReadSetting(name, value).value<Type>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Config::ReadGlobalSetting(Settings::SwitchableSetting<std::string>& setting) {
|
||||
QString name = QString::fromStdString(setting.GetLabel());
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
setting.SetGlobal(use_global);
|
||||
if (global || !use_global) {
|
||||
const QString default_value = QString::fromStdString(setting.GetDefault());
|
||||
setting.SetValue(
|
||||
ReadSetting(name, QVariant::fromValue(default_value)).toString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant
|
||||
template <>
|
||||
void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const std::string& value = setting.GetValue();
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
qt_config->setValue(name, QString::fromStdString(value));
|
||||
}
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void Config::WriteBasicSetting(const Settings::Setting<Type, ranged>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const Type value = setting.GetValue();
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
if constexpr (std::is_enum_v<Type>) {
|
||||
qt_config->setValue(name, static_cast<std::underlying_type_t<Type>>(value));
|
||||
} else {
|
||||
qt_config->setValue(name, QVariant::fromValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const Type& value = setting.GetValue(global);
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
if constexpr (std::is_enum_v<Type>) {
|
||||
qt_config->setValue(name, static_cast<std::underlying_type_t<Type>>(value));
|
||||
} else {
|
||||
qt_config->setValue(name, QVariant::fromValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Config::WriteGlobalSetting(const Settings::SwitchableSetting<std::string>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const std::string& value = setting.GetValue(global);
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
qt_config->setValue(name, QString::fromStdString(value));
|
||||
}
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
if (global) {
|
||||
ReadControlValues();
|
||||
ReadCoreValues();
|
||||
ReadRendererValues();
|
||||
ReadLayoutValues();
|
||||
ReadAudioValues();
|
||||
ReadCameraValues();
|
||||
ReadDataStorageValues();
|
||||
ReadSystemValues();
|
||||
ReadMiscellaneousValues();
|
||||
ReadDebuggingValues();
|
||||
ReadWebServiceValues();
|
||||
ReadVideoDumpingValues();
|
||||
ReadUIValues();
|
||||
ReadUtilityValues();
|
||||
}
|
||||
|
||||
ReadUIValues();
|
||||
ReadCoreValues();
|
||||
ReadRendererValues();
|
||||
ReadLayoutValues();
|
||||
ReadAudioValues();
|
||||
ReadSystemValues();
|
||||
}
|
||||
|
||||
void Config::ReadAudioValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||
|
||||
Settings::values.enable_dsp_lle = ReadSetting(QStringLiteral("enable_dsp_lle"), false).toBool();
|
||||
Settings::values.enable_dsp_lle_multithread =
|
||||
ReadSetting(QStringLiteral("enable_dsp_lle_multithread"), false).toBool();
|
||||
Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.enable_audio_stretching =
|
||||
ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
|
||||
Settings::values.audio_device_id =
|
||||
ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
|
||||
Settings::values.mic_input_type = static_cast<Settings::MicInputType>(
|
||||
ReadSetting(QStringLiteral("mic_input_type"), 0).toInt());
|
||||
Settings::values.mic_input_device =
|
||||
ReadSetting(QStringLiteral("mic_input_device"),
|
||||
QString::fromUtf8(Frontend::Mic::default_device_name))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadGlobalSetting(Settings::values.audio_emulation);
|
||||
ReadGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
ReadGlobalSetting(Settings::values.volume);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.sink_id);
|
||||
ReadBasicSetting(Settings::values.audio_device_id);
|
||||
ReadBasicSetting(Settings::values.mic_input_device);
|
||||
ReadBasicSetting(Settings::values.mic_input_type);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -280,11 +409,9 @@ void Config::ReadControlValues() {
|
||||
void Config::ReadUtilityValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Utility"));
|
||||
|
||||
Settings::values.dump_textures = ReadSetting(QStringLiteral("dump_textures"), false).toBool();
|
||||
Settings::values.custom_textures =
|
||||
ReadSetting(QStringLiteral("custom_textures"), false).toBool();
|
||||
Settings::values.preload_textures =
|
||||
ReadSetting(QStringLiteral("preload_textures"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.dump_textures);
|
||||
ReadBasicSetting(Settings::values.custom_textures);
|
||||
ReadBasicSetting(Settings::values.preload_textures);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -292,9 +419,11 @@ void Config::ReadUtilityValues() {
|
||||
void Config::ReadCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool();
|
||||
Settings::values.cpu_clock_percentage =
|
||||
ReadSetting(QStringLiteral("cpu_clock_percentage"), 100).toInt();
|
||||
ReadGlobalSetting(Settings::values.cpu_clock_percentage);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.use_cpu_jit);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -302,10 +431,9 @@ void Config::ReadCoreValues() {
|
||||
void Config::ReadDataStorageValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Data Storage"));
|
||||
|
||||
Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
|
||||
ReadBasicSetting(Settings::values.use_virtual_sd);
|
||||
ReadBasicSetting(Settings::values.use_custom_storage);
|
||||
|
||||
Settings::values.use_custom_storage =
|
||||
ReadSetting(QStringLiteral("use_custom_storage"), false).toBool();
|
||||
const std::string nand_dir =
|
||||
ReadSetting(QStringLiteral("nand_directory"), QStringLiteral("")).toString().toStdString();
|
||||
const std::string sdmc_dir =
|
||||
@ -325,8 +453,10 @@ void Config::ReadDebuggingValues() {
|
||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||
Settings::values.record_frame_times =
|
||||
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
|
||||
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
|
||||
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
|
||||
ReadBasicSetting(Settings::values.use_gdbstub);
|
||||
ReadBasicSetting(Settings::values.gdbstub_port);
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
ReadBasicSetting(Settings::values.dump_command_buffers);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Service::service_module_map) {
|
||||
@ -334,43 +464,38 @@ void Config::ReadDebuggingValues() {
|
||||
Settings::values.lle_modules.emplace(service_module.name, use_lle);
|
||||
}
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::ReadLayoutValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Layout"));
|
||||
|
||||
Settings::values.render_3d = static_cast<Settings::StereoRenderOption>(
|
||||
ReadSetting(QStringLiteral("render_3d"), 0).toInt());
|
||||
Settings::values.factor_3d = ReadSetting(QStringLiteral("factor_3d"), 0).toInt();
|
||||
ReadGlobalSetting(Settings::values.render_3d);
|
||||
ReadGlobalSetting(Settings::values.factor_3d);
|
||||
Settings::values.pp_shader_name =
|
||||
ReadSetting(QStringLiteral("pp_shader_name"),
|
||||
(Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph)
|
||||
ReadSetting(QStringLiteral("pp_shader_name"), (Settings::values.render_3d.GetValue() ==
|
||||
Settings::StereoRenderOption::Anaglyph)
|
||||
? QStringLiteral("dubois (builtin)")
|
||||
: QStringLiteral("none (builtin)"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.filter_mode = ReadSetting(QStringLiteral("filter_mode"), true).toBool();
|
||||
Settings::values.layout_option =
|
||||
static_cast<Settings::LayoutOption>(ReadSetting(QStringLiteral("layout_option")).toInt());
|
||||
Settings::values.swap_screen = ReadSetting(QStringLiteral("swap_screen"), false).toBool();
|
||||
Settings::values.upright_screen = ReadSetting(QStringLiteral("upright_screen"), false).toBool();
|
||||
Settings::values.custom_layout = ReadSetting(QStringLiteral("custom_layout"), false).toBool();
|
||||
Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt();
|
||||
Settings::values.custom_top_top = ReadSetting(QStringLiteral("custom_top_top"), 0).toInt();
|
||||
Settings::values.custom_top_right =
|
||||
ReadSetting(QStringLiteral("custom_top_right"), 400).toInt();
|
||||
Settings::values.custom_top_bottom =
|
||||
ReadSetting(QStringLiteral("custom_top_bottom"), 240).toInt();
|
||||
Settings::values.custom_bottom_left =
|
||||
ReadSetting(QStringLiteral("custom_bottom_left"), 40).toInt();
|
||||
Settings::values.custom_bottom_top =
|
||||
ReadSetting(QStringLiteral("custom_bottom_top"), 240).toInt();
|
||||
Settings::values.custom_bottom_right =
|
||||
ReadSetting(QStringLiteral("custom_bottom_right"), 360).toInt();
|
||||
Settings::values.custom_bottom_bottom =
|
||||
ReadSetting(QStringLiteral("custom_bottom_bottom"), 480).toInt();
|
||||
ReadGlobalSetting(Settings::values.filter_mode);
|
||||
ReadGlobalSetting(Settings::values.layout_option);
|
||||
ReadGlobalSetting(Settings::values.swap_screen);
|
||||
ReadGlobalSetting(Settings::values.upright_screen);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.mono_render_option);
|
||||
ReadBasicSetting(Settings::values.custom_layout);
|
||||
ReadBasicSetting(Settings::values.custom_top_left);
|
||||
ReadBasicSetting(Settings::values.custom_top_top);
|
||||
ReadBasicSetting(Settings::values.custom_top_right);
|
||||
ReadBasicSetting(Settings::values.custom_top_bottom);
|
||||
ReadBasicSetting(Settings::values.custom_bottom_left);
|
||||
ReadBasicSetting(Settings::values.custom_bottom_top);
|
||||
ReadBasicSetting(Settings::values.custom_bottom_right);
|
||||
ReadBasicSetting(Settings::values.custom_bottom_bottom);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -378,10 +503,7 @@ void Config::ReadLayoutValues() {
|
||||
void Config::ReadMiscellaneousValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Miscellaneous"));
|
||||
|
||||
Settings::values.log_filter =
|
||||
ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadBasicSetting(Settings::values.log_filter);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -431,13 +553,15 @@ void Config::ReadMultiplayerValues() {
|
||||
void Config::ReadPathValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Paths"));
|
||||
|
||||
ReadGlobalSetting(UISettings::values.screenshot_path);
|
||||
|
||||
if (global) {
|
||||
UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
|
||||
UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
|
||||
UISettings::values.movie_record_path =
|
||||
ReadSetting(QStringLiteral("movieRecordPath")).toString();
|
||||
UISettings::values.movie_playback_path =
|
||||
ReadSetting(QStringLiteral("moviePlaybackPath")).toString();
|
||||
UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString();
|
||||
UISettings::values.video_dumping_path =
|
||||
ReadSetting(QStringLiteral("videoDumpingPath")).toString();
|
||||
UISettings::values.game_dir_deprecated =
|
||||
@ -471,6 +595,7 @@ void Config::ReadPathValues() {
|
||||
}
|
||||
UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
|
||||
UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -478,38 +603,33 @@ void Config::ReadPathValues() {
|
||||
void Config::ReadRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
Settings::values.use_hw_renderer =
|
||||
ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
|
||||
Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool();
|
||||
ReadGlobalSetting(Settings::values.physical_device);
|
||||
ReadGlobalSetting(Settings::values.async_command_recording);
|
||||
ReadGlobalSetting(Settings::values.spirv_shader_gen);
|
||||
ReadGlobalSetting(Settings::values.graphics_api);
|
||||
ReadGlobalSetting(Settings::values.use_hw_renderer);
|
||||
ReadGlobalSetting(Settings::values.use_hw_shader);
|
||||
#ifdef __APPLE__
|
||||
// Hardware shader is broken on macos with Intel GPUs thanks to poor drivers.
|
||||
// We still want to provide this option for test/development purposes, but disable it by
|
||||
// default.
|
||||
Settings::values.separable_shader =
|
||||
ReadSetting(QStringLiteral("separable_shader"), false).toBool();
|
||||
ReadGlobalSetting(Settings::values.separable_shader);
|
||||
#endif
|
||||
Settings::values.shaders_accurate_mul =
|
||||
ReadSetting(QStringLiteral("shaders_accurate_mul"), true).toBool();
|
||||
Settings::values.use_shader_jit = ReadSetting(QStringLiteral("use_shader_jit"), true).toBool();
|
||||
Settings::values.use_disk_shader_cache =
|
||||
ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
|
||||
Settings::values.use_vsync_new = ReadSetting(QStringLiteral("use_vsync_new"), true).toBool();
|
||||
Settings::values.resolution_factor =
|
||||
static_cast<u16>(ReadSetting(QStringLiteral("resolution_factor"), 1).toInt());
|
||||
Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
|
||||
Settings::values.use_frame_limit_alternate =
|
||||
ReadSetting(QStringLiteral("use_frame_limit_alternate"), false).toBool();
|
||||
Settings::values.frame_limit_alternate =
|
||||
ReadSetting(QStringLiteral("frame_limit_alternate"), 200).toInt();
|
||||
ReadGlobalSetting(Settings::values.shaders_accurate_mul);
|
||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
ReadGlobalSetting(Settings::values.use_vsync_new);
|
||||
ReadGlobalSetting(Settings::values.resolution_factor);
|
||||
ReadGlobalSetting(Settings::values.frame_limit);
|
||||
|
||||
Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
|
||||
Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
|
||||
Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
|
||||
ReadGlobalSetting(Settings::values.bg_red);
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
Settings::values.texture_filter_name =
|
||||
ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadGlobalSetting(Settings::values.texture_filter_name);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.use_shader_jit);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -517,7 +637,7 @@ void Config::ReadRendererValues() {
|
||||
void Config::ReadShortcutValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Shortcuts"));
|
||||
|
||||
for (auto [name, group, shortcut] : default_hotkeys) {
|
||||
for (const auto& [name, group, shortcut] : default_hotkeys) {
|
||||
auto [keyseq, context] = shortcut;
|
||||
qt_config->beginGroup(group);
|
||||
qt_config->beginGroup(name);
|
||||
@ -536,14 +656,16 @@ void Config::ReadShortcutValues() {
|
||||
void Config::ReadSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
Settings::values.is_new_3ds = ReadSetting(QStringLiteral("is_new_3ds"), true).toBool();
|
||||
Settings::values.region_value =
|
||||
ReadSetting(QStringLiteral("region_value"), Settings::REGION_VALUE_AUTO_SELECT).toInt();
|
||||
Settings::values.init_clock = static_cast<Settings::InitClock>(
|
||||
ReadSetting(QStringLiteral("init_clock"), static_cast<u32>(Settings::InitClock::SystemTime))
|
||||
.toInt());
|
||||
Settings::values.init_time =
|
||||
ReadSetting(QStringLiteral("init_time"), 946681277ULL).toULongLong();
|
||||
ReadGlobalSetting(Settings::values.is_new_3ds);
|
||||
ReadGlobalSetting(Settings::values.region_value);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.init_clock);
|
||||
ReadBasicSetting(Settings::values.init_time);
|
||||
ReadBasicSetting(Settings::values.init_time_offset);
|
||||
ReadBasicSetting(Settings::values.plugin_loader_enabled);
|
||||
ReadBasicSetting(Settings::values.allow_plugin_loader);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -552,7 +674,7 @@ void Config::ReadSystemValues() {
|
||||
// https://developers.google.com/media/vp9/live-encoding
|
||||
const QString DEFAULT_VIDEO_ENCODER_OPTIONS =
|
||||
QStringLiteral("quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1");
|
||||
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QString{};
|
||||
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QStringLiteral("");
|
||||
|
||||
void Config::ReadVideoDumpingValues() {
|
||||
qt_config->beginGroup(QStringLiteral("VideoDumping"));
|
||||
@ -594,39 +716,33 @@ void Config::ReadVideoDumpingValues() {
|
||||
void Config::ReadUIValues() {
|
||||
qt_config->beginGroup(QStringLiteral("UI"));
|
||||
|
||||
ReadPathValues();
|
||||
|
||||
if (global) {
|
||||
UISettings::values.theme =
|
||||
ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second))
|
||||
.toString();
|
||||
UISettings::values.enable_discord_presence =
|
||||
ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool();
|
||||
UISettings::values.screenshot_resolution_factor =
|
||||
static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt());
|
||||
ReadBasicSetting(UISettings::values.enable_discord_presence);
|
||||
ReadBasicSetting(UISettings::values.screenshot_resolution_factor);
|
||||
|
||||
ReadUpdaterValues();
|
||||
ReadUILayoutValues();
|
||||
ReadUIGameListValues();
|
||||
ReadPathValues();
|
||||
ReadShortcutValues();
|
||||
ReadMultiplayerValues();
|
||||
|
||||
UISettings::values.single_window_mode =
|
||||
ReadSetting(QStringLiteral("singleWindowMode"), true).toBool();
|
||||
UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool();
|
||||
UISettings::values.display_titlebar =
|
||||
ReadSetting(QStringLiteral("displayTitleBars"), true).toBool();
|
||||
UISettings::values.show_filter_bar =
|
||||
ReadSetting(QStringLiteral("showFilterBar"), true).toBool();
|
||||
UISettings::values.show_status_bar =
|
||||
ReadSetting(QStringLiteral("showStatusBar"), true).toBool();
|
||||
UISettings::values.confirm_before_closing =
|
||||
ReadSetting(QStringLiteral("confirmClose"), true).toBool();
|
||||
UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
|
||||
UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
|
||||
UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
|
||||
UISettings::values.pause_when_in_background =
|
||||
ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
|
||||
UISettings::values.hide_mouse =
|
||||
ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.single_window_mode);
|
||||
ReadBasicSetting(UISettings::values.fullscreen);
|
||||
ReadBasicSetting(UISettings::values.display_titlebar);
|
||||
ReadBasicSetting(UISettings::values.show_filter_bar);
|
||||
ReadBasicSetting(UISettings::values.show_status_bar);
|
||||
ReadBasicSetting(UISettings::values.confirm_before_closing);
|
||||
ReadBasicSetting(UISettings::values.first_start);
|
||||
ReadBasicSetting(UISettings::values.callout_flags);
|
||||
ReadBasicSetting(UISettings::values.show_console);
|
||||
ReadBasicSetting(UISettings::values.pause_when_in_background);
|
||||
ReadBasicSetting(UISettings::values.hide_mouse);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -634,36 +750,11 @@ void Config::ReadUIValues() {
|
||||
void Config::ReadUIGameListValues() {
|
||||
qt_config->beginGroup(QStringLiteral("GameList"));
|
||||
|
||||
auto icon_size = UISettings::GameListIconSize{
|
||||
ReadSetting(QStringLiteral("iconSize"),
|
||||
static_cast<int>(UISettings::GameListIconSize::LargeIcon))
|
||||
.toInt()};
|
||||
if (icon_size < UISettings::GameListIconSize::NoIcon ||
|
||||
icon_size > UISettings::GameListIconSize::LargeIcon) {
|
||||
icon_size = UISettings::GameListIconSize::LargeIcon;
|
||||
}
|
||||
UISettings::values.game_list_icon_size = icon_size;
|
||||
|
||||
UISettings::GameListText row_1 = UISettings::GameListText{
|
||||
ReadSetting(QStringLiteral("row1"), static_cast<int>(UISettings::GameListText::TitleName))
|
||||
.toInt()};
|
||||
if (row_1 <= UISettings::GameListText::NoText || row_1 >= UISettings::GameListText::ListEnd) {
|
||||
row_1 = UISettings::GameListText::TitleName;
|
||||
}
|
||||
UISettings::values.game_list_row_1 = row_1;
|
||||
|
||||
UISettings::GameListText row_2 = UISettings::GameListText{
|
||||
ReadSetting(QStringLiteral("row2"), static_cast<int>(UISettings::GameListText::FileName))
|
||||
.toInt()};
|
||||
if (row_2 < UISettings::GameListText::NoText || row_2 >= UISettings::GameListText::ListEnd) {
|
||||
row_2 = UISettings::GameListText::FileName;
|
||||
}
|
||||
UISettings::values.game_list_row_2 = row_2;
|
||||
|
||||
UISettings::values.game_list_hide_no_icon =
|
||||
ReadSetting(QStringLiteral("hideNoIcon"), false).toBool();
|
||||
UISettings::values.game_list_single_line_mode =
|
||||
ReadSetting(QStringLiteral("singleLineMode"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.game_list_icon_size);
|
||||
ReadBasicSetting(UISettings::values.game_list_row_1);
|
||||
ReadBasicSetting(UISettings::values.game_list_row_2);
|
||||
ReadBasicSetting(UISettings::values.game_list_hide_no_icon);
|
||||
ReadBasicSetting(UISettings::values.game_list_single_line_mode);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -679,8 +770,7 @@ void Config::ReadUILayoutValues() {
|
||||
ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
|
||||
UISettings::values.microprofile_geometry =
|
||||
ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
|
||||
UISettings::values.microprofile_visible =
|
||||
ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.microprofile_visible);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -688,10 +778,8 @@ void Config::ReadUILayoutValues() {
|
||||
void Config::ReadUpdaterValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Updater"));
|
||||
|
||||
UISettings::values.check_for_update_on_start =
|
||||
ReadSetting(QStringLiteral("check_for_update_on_start"), true).toBool();
|
||||
UISettings::values.update_on_close =
|
||||
ReadSetting(QStringLiteral("update_on_close"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.check_for_update_on_start);
|
||||
ReadBasicSetting(UISettings::values.update_on_close);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -714,40 +802,38 @@ void Config::ReadWebServiceValues() {
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
if (global) {
|
||||
SaveControlValues();
|
||||
SaveCoreValues();
|
||||
SaveRendererValues();
|
||||
SaveLayoutValues();
|
||||
SaveAudioValues();
|
||||
SaveCameraValues();
|
||||
SaveDataStorageValues();
|
||||
SaveSystemValues();
|
||||
SaveMiscellaneousValues();
|
||||
SaveDebuggingValues();
|
||||
SaveWebServiceValues();
|
||||
SaveVideoDumpingValues();
|
||||
SaveUIValues();
|
||||
SaveUtilityValues();
|
||||
}
|
||||
|
||||
SaveUIValues();
|
||||
SaveCoreValues();
|
||||
SaveRendererValues();
|
||||
SaveLayoutValues();
|
||||
SaveAudioValues();
|
||||
SaveSystemValues();
|
||||
}
|
||||
|
||||
void Config::SaveAudioValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||
|
||||
WriteSetting(QStringLiteral("enable_dsp_lle"), Settings::values.enable_dsp_lle, false);
|
||||
WriteSetting(QStringLiteral("enable_dsp_lle_multithread"),
|
||||
Settings::values.enable_dsp_lle_multithread, false);
|
||||
WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
|
||||
QStringLiteral("auto"));
|
||||
WriteSetting(QStringLiteral("enable_audio_stretching"),
|
||||
Settings::values.enable_audio_stretching, true);
|
||||
WriteSetting(QStringLiteral("output_device"),
|
||||
QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
|
||||
WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
|
||||
WriteSetting(QStringLiteral("mic_input_device"),
|
||||
QString::fromStdString(Settings::values.mic_input_device),
|
||||
QString::fromUtf8(Frontend::Mic::default_device_name));
|
||||
WriteSetting(QStringLiteral("mic_input_type"),
|
||||
static_cast<int>(Settings::values.mic_input_type), 0);
|
||||
WriteGlobalSetting(Settings::values.audio_emulation);
|
||||
WriteGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
WriteGlobalSetting(Settings::values.volume);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.sink_id);
|
||||
WriteBasicSetting(Settings::values.audio_device_id);
|
||||
WriteBasicSetting(Settings::values.mic_input_device);
|
||||
WriteBasicSetting(Settings::values.mic_input_type);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -844,9 +930,9 @@ void Config::SaveControlValues() {
|
||||
void Config::SaveUtilityValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Utility"));
|
||||
|
||||
WriteSetting(QStringLiteral("dump_textures"), Settings::values.dump_textures, false);
|
||||
WriteSetting(QStringLiteral("custom_textures"), Settings::values.custom_textures, false);
|
||||
WriteSetting(QStringLiteral("preload_textures"), Settings::values.preload_textures, false);
|
||||
WriteBasicSetting(Settings::values.dump_textures);
|
||||
WriteBasicSetting(Settings::values.custom_textures);
|
||||
WriteBasicSetting(Settings::values.preload_textures);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -854,9 +940,11 @@ void Config::SaveUtilityValues() {
|
||||
void Config::SaveCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true);
|
||||
WriteSetting(QStringLiteral("cpu_clock_percentage"), Settings::values.cpu_clock_percentage,
|
||||
100);
|
||||
WriteGlobalSetting(Settings::values.cpu_clock_percentage);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.use_cpu_jit);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -864,8 +952,8 @@ void Config::SaveCoreValues() {
|
||||
void Config::SaveDataStorageValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Data Storage"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
|
||||
WriteSetting(QStringLiteral("use_custom_storage"), Settings::values.use_custom_storage, false);
|
||||
WriteBasicSetting(Settings::values.use_virtual_sd);
|
||||
WriteBasicSetting(Settings::values.use_custom_storage);
|
||||
WriteSetting(QStringLiteral("nand_directory"),
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
|
||||
QStringLiteral(""));
|
||||
@ -881,8 +969,10 @@ void Config::SaveDebuggingValues() {
|
||||
|
||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
|
||||
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
|
||||
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
|
||||
WriteBasicSetting(Settings::values.use_gdbstub);
|
||||
WriteBasicSetting(Settings::values.gdbstub_port);
|
||||
WriteBasicSetting(Settings::values.renderer_debug);
|
||||
WriteBasicSetting(Settings::values.dump_command_buffers);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Settings::values.lle_modules) {
|
||||
@ -896,27 +986,30 @@ void Config::SaveDebuggingValues() {
|
||||
void Config::SaveLayoutValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Layout"));
|
||||
|
||||
WriteSetting(QStringLiteral("render_3d"), static_cast<int>(Settings::values.render_3d), 0);
|
||||
WriteSetting(QStringLiteral("factor_3d"), Settings::values.factor_3d.load(), 0);
|
||||
WriteGlobalSetting(Settings::values.render_3d);
|
||||
WriteGlobalSetting(Settings::values.factor_3d);
|
||||
WriteSetting(QStringLiteral("pp_shader_name"),
|
||||
QString::fromStdString(Settings::values.pp_shader_name),
|
||||
(Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph)
|
||||
QString::fromStdString(Settings::values.pp_shader_name.GetValue()),
|
||||
(Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph)
|
||||
? QStringLiteral("dubois (builtin)")
|
||||
: QStringLiteral("none (builtin)"));
|
||||
WriteSetting(QStringLiteral("filter_mode"), Settings::values.filter_mode, true);
|
||||
WriteSetting(QStringLiteral("layout_option"), static_cast<int>(Settings::values.layout_option));
|
||||
WriteSetting(QStringLiteral("swap_screen"), Settings::values.swap_screen, false);
|
||||
WriteSetting(QStringLiteral("upright_screen"), Settings::values.upright_screen, false);
|
||||
WriteSetting(QStringLiteral("custom_layout"), Settings::values.custom_layout, false);
|
||||
WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0);
|
||||
WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0);
|
||||
WriteSetting(QStringLiteral("custom_top_right"), Settings::values.custom_top_right, 400);
|
||||
WriteSetting(QStringLiteral("custom_top_bottom"), Settings::values.custom_top_bottom, 240);
|
||||
WriteSetting(QStringLiteral("custom_bottom_left"), Settings::values.custom_bottom_left, 40);
|
||||
WriteSetting(QStringLiteral("custom_bottom_top"), Settings::values.custom_bottom_top, 240);
|
||||
WriteSetting(QStringLiteral("custom_bottom_right"), Settings::values.custom_bottom_right, 360);
|
||||
WriteSetting(QStringLiteral("custom_bottom_bottom"), Settings::values.custom_bottom_bottom,
|
||||
480);
|
||||
WriteGlobalSetting(Settings::values.filter_mode);
|
||||
WriteGlobalSetting(Settings::values.layout_option);
|
||||
WriteGlobalSetting(Settings::values.swap_screen);
|
||||
WriteGlobalSetting(Settings::values.upright_screen);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.mono_render_option);
|
||||
WriteBasicSetting(Settings::values.custom_layout);
|
||||
WriteBasicSetting(Settings::values.custom_top_left);
|
||||
WriteBasicSetting(Settings::values.custom_top_top);
|
||||
WriteBasicSetting(Settings::values.custom_top_right);
|
||||
WriteBasicSetting(Settings::values.custom_top_bottom);
|
||||
WriteBasicSetting(Settings::values.custom_bottom_left);
|
||||
WriteBasicSetting(Settings::values.custom_bottom_top);
|
||||
WriteBasicSetting(Settings::values.custom_bottom_right);
|
||||
WriteBasicSetting(Settings::values.custom_bottom_bottom);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -924,8 +1017,7 @@ void Config::SaveLayoutValues() {
|
||||
void Config::SaveMiscellaneousValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Miscellaneous"));
|
||||
|
||||
WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter),
|
||||
QStringLiteral("*:Info"));
|
||||
WriteBasicSetting(Settings::values.log_filter);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -967,11 +1059,12 @@ void Config::SaveMultiplayerValues() {
|
||||
void Config::SavePathValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Paths"));
|
||||
|
||||
WriteGlobalSetting(UISettings::values.screenshot_path);
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
|
||||
WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
|
||||
WriteSetting(QStringLiteral("movieRecordPath"), UISettings::values.movie_record_path);
|
||||
WriteSetting(QStringLiteral("moviePlaybackPath"), UISettings::values.movie_playback_path);
|
||||
WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path);
|
||||
WriteSetting(QStringLiteral("videoDumpingPath"), UISettings::values.video_dumping_path);
|
||||
qt_config->beginWriteArray(QStringLiteral("gamedirs"));
|
||||
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
|
||||
@ -984,6 +1077,7 @@ void Config::SavePathValues() {
|
||||
qt_config->endArray();
|
||||
WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
|
||||
WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -991,34 +1085,33 @@ void Config::SavePathValues() {
|
||||
void Config::SaveRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
|
||||
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
|
||||
WriteGlobalSetting(Settings::values.graphics_api);
|
||||
WriteGlobalSetting(Settings::values.physical_device);
|
||||
WriteGlobalSetting(Settings::values.async_command_recording);
|
||||
WriteGlobalSetting(Settings::values.spirv_shader_gen);
|
||||
WriteGlobalSetting(Settings::values.use_hw_renderer);
|
||||
WriteGlobalSetting(Settings::values.use_hw_shader);
|
||||
#ifdef __APPLE__
|
||||
// Hardware shader is broken on macos thanks to poor drivers.
|
||||
// TODO: enable this for none Intel GPUs
|
||||
WriteSetting(QStringLiteral("use_separable_shader"), Settings::values.separable_shader, false);
|
||||
WriteGlobalSetting(Settings::values.separable_shader);
|
||||
#endif
|
||||
WriteSetting(QStringLiteral("shaders_accurate_mul"), Settings::values.shaders_accurate_mul,
|
||||
true);
|
||||
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit, true);
|
||||
WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
|
||||
true);
|
||||
WriteSetting(QStringLiteral("use_vsync_new"), Settings::values.use_vsync_new, true);
|
||||
WriteSetting(QStringLiteral("resolution_factor"), Settings::values.resolution_factor, 1);
|
||||
WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
|
||||
WriteSetting(QStringLiteral("use_frame_limit_alternate"),
|
||||
Settings::values.use_frame_limit_alternate, false);
|
||||
WriteSetting(QStringLiteral("frame_limit_alternate"), Settings::values.frame_limit_alternate,
|
||||
200);
|
||||
WriteGlobalSetting(Settings::values.shaders_accurate_mul);
|
||||
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
WriteGlobalSetting(Settings::values.use_vsync_new);
|
||||
WriteGlobalSetting(Settings::values.resolution_factor);
|
||||
WriteGlobalSetting(Settings::values.frame_limit);
|
||||
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
WriteSetting(QStringLiteral("bg_red"), (double)Settings::values.bg_red, 0.0);
|
||||
WriteSetting(QStringLiteral("bg_green"), (double)Settings::values.bg_green, 0.0);
|
||||
WriteSetting(QStringLiteral("bg_blue"), (double)Settings::values.bg_blue, 0.0);
|
||||
WriteGlobalSetting(Settings::values.bg_red);
|
||||
WriteGlobalSetting(Settings::values.bg_green);
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
WriteSetting(QStringLiteral("texture_filter_name"),
|
||||
QString::fromStdString(Settings::values.texture_filter_name),
|
||||
QStringLiteral("none"));
|
||||
WriteGlobalSetting(Settings::values.texture_filter_name);
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
|
||||
true);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1045,13 +1138,16 @@ void Config::SaveShortcutValues() {
|
||||
void Config::SaveSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
WriteSetting(QStringLiteral("is_new_3ds"), Settings::values.is_new_3ds, true);
|
||||
WriteSetting(QStringLiteral("region_value"), Settings::values.region_value,
|
||||
Settings::REGION_VALUE_AUTO_SELECT);
|
||||
WriteSetting(QStringLiteral("init_clock"), static_cast<u32>(Settings::values.init_clock),
|
||||
static_cast<u32>(Settings::InitClock::SystemTime));
|
||||
WriteSetting(QStringLiteral("init_time"),
|
||||
static_cast<unsigned long long>(Settings::values.init_time), 946681277ULL);
|
||||
WriteGlobalSetting(Settings::values.is_new_3ds);
|
||||
WriteGlobalSetting(Settings::values.region_value);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.init_clock);
|
||||
WriteBasicSetting(Settings::values.init_time);
|
||||
WriteBasicSetting(Settings::values.init_time_offset);
|
||||
WriteBasicSetting(Settings::values.plugin_loader_enabled);
|
||||
WriteBasicSetting(Settings::values.allow_plugin_loader);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1086,32 +1182,32 @@ void Config::SaveVideoDumpingValues() {
|
||||
void Config::SaveUIValues() {
|
||||
qt_config->beginGroup(QStringLiteral("UI"));
|
||||
|
||||
SavePathValues();
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
|
||||
QString::fromUtf8(UISettings::themes[0].second));
|
||||
WriteSetting(QStringLiteral("enable_discord_presence"),
|
||||
UISettings::values.enable_discord_presence, true);
|
||||
WriteSetting(QStringLiteral("screenshot_resolution_factor"),
|
||||
UISettings::values.screenshot_resolution_factor, 0);
|
||||
WriteBasicSetting(UISettings::values.enable_discord_presence);
|
||||
WriteBasicSetting(UISettings::values.screenshot_resolution_factor);
|
||||
|
||||
SaveUpdaterValues();
|
||||
SaveUILayoutValues();
|
||||
SaveUIGameListValues();
|
||||
SavePathValues();
|
||||
SaveShortcutValues();
|
||||
SaveMultiplayerValues();
|
||||
|
||||
WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true);
|
||||
WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false);
|
||||
WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true);
|
||||
WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true);
|
||||
WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true);
|
||||
WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true);
|
||||
WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
|
||||
WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
|
||||
WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
|
||||
WriteSetting(QStringLiteral("pauseWhenInBackground"),
|
||||
UISettings::values.pause_when_in_background, false);
|
||||
WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
|
||||
WriteBasicSetting(UISettings::values.single_window_mode);
|
||||
WriteBasicSetting(UISettings::values.fullscreen);
|
||||
WriteBasicSetting(UISettings::values.display_titlebar);
|
||||
WriteBasicSetting(UISettings::values.show_filter_bar);
|
||||
WriteBasicSetting(UISettings::values.show_status_bar);
|
||||
WriteBasicSetting(UISettings::values.confirm_before_closing);
|
||||
WriteBasicSetting(UISettings::values.first_start);
|
||||
WriteBasicSetting(UISettings::values.callout_flags);
|
||||
WriteBasicSetting(UISettings::values.show_console);
|
||||
WriteBasicSetting(UISettings::values.pause_when_in_background);
|
||||
WriteBasicSetting(UISettings::values.hide_mouse);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1119,13 +1215,11 @@ void Config::SaveUIValues() {
|
||||
void Config::SaveUIGameListValues() {
|
||||
qt_config->beginGroup(QStringLiteral("GameList"));
|
||||
|
||||
WriteSetting(QStringLiteral("iconSize"),
|
||||
static_cast<int>(UISettings::values.game_list_icon_size), 2);
|
||||
WriteSetting(QStringLiteral("row1"), static_cast<int>(UISettings::values.game_list_row_1), 2);
|
||||
WriteSetting(QStringLiteral("row2"), static_cast<int>(UISettings::values.game_list_row_2), 0);
|
||||
WriteSetting(QStringLiteral("hideNoIcon"), UISettings::values.game_list_hide_no_icon, false);
|
||||
WriteSetting(QStringLiteral("singleLineMode"), UISettings::values.game_list_single_line_mode,
|
||||
false);
|
||||
WriteBasicSetting(UISettings::values.game_list_icon_size);
|
||||
WriteBasicSetting(UISettings::values.game_list_row_1);
|
||||
WriteBasicSetting(UISettings::values.game_list_row_2);
|
||||
WriteBasicSetting(UISettings::values.game_list_hide_no_icon);
|
||||
WriteBasicSetting(UISettings::values.game_list_single_line_mode);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1139,8 +1233,7 @@ void Config::SaveUILayoutValues() {
|
||||
WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
|
||||
WriteSetting(QStringLiteral("microProfileDialogGeometry"),
|
||||
UISettings::values.microprofile_geometry);
|
||||
WriteSetting(QStringLiteral("microProfileDialogVisible"),
|
||||
UISettings::values.microprofile_visible, false);
|
||||
WriteBasicSetting(UISettings::values.microprofile_visible);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1148,9 +1241,8 @@ void Config::SaveUILayoutValues() {
|
||||
void Config::SaveUpdaterValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Updater"));
|
||||
|
||||
WriteSetting(QStringLiteral("check_for_update_on_start"),
|
||||
UISettings::values.check_for_update_on_start, true);
|
||||
WriteSetting(QStringLiteral("update_on_close"), UISettings::values.update_on_close, false);
|
||||
WriteBasicSetting(UISettings::values.check_for_update_on_start);
|
||||
WriteBasicSetting(UISettings::values.update_on_close);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1184,6 +1276,15 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSettingGlobal(Type& setting, const QString& name,
|
||||
const QVariant& default_value) const {
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
if (global || !use_global) {
|
||||
setting = ReadSetting(name, default_value).value<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::WriteSetting(const QString& name, const QVariant& value) {
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
@ -1194,11 +1295,21 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
|
||||
void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
|
||||
bool use_global) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
|
||||
}
|
||||
if (global || !use_global) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Config::Reload() {
|
||||
ReadValues();
|
||||
// To apply default value changes
|
||||
SaveValues();
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void Config::Save() {
|
||||
|
@ -8,13 +8,16 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QVariant>
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
class QSettings;
|
||||
|
||||
class Config {
|
||||
public:
|
||||
Config();
|
||||
enum class ConfigType : u32 { GlobalConfig, PerGameConfig };
|
||||
|
||||
explicit Config(const std::string& config_name = "qt-config",
|
||||
ConfigType config_type = ConfigType::GlobalConfig);
|
||||
~Config();
|
||||
|
||||
void Reload();
|
||||
@ -24,6 +27,8 @@ public:
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||
|
||||
private:
|
||||
void Initialize(const std::string& config_name);
|
||||
|
||||
void ReadValues();
|
||||
void ReadAudioValues();
|
||||
void ReadCameraValues();
|
||||
@ -68,11 +73,78 @@ private:
|
||||
void SaveWebServiceValues();
|
||||
void SaveVideoDumpingValues();
|
||||
|
||||
/**
|
||||
* Reads a setting from the qt_config.
|
||||
*
|
||||
* @param name The setting's identifier
|
||||
* @param default_value The value to use when the setting is not already present in the config
|
||||
*/
|
||||
QVariant ReadSetting(const QString& name) const;
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
|
||||
|
||||
/**
|
||||
* Only reads a setting from the qt_config if the current config is a global config, or if the
|
||||
* current config is a custom config and the setting is overriding the global setting. Otherwise
|
||||
* it does nothing.
|
||||
*
|
||||
* @param setting The variable to be modified
|
||||
* @param name The setting's identifier
|
||||
* @param default_value The value to use when the setting is not already present in the config
|
||||
*/
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
|
||||
|
||||
/**
|
||||
* Writes a setting to the qt_config.
|
||||
*
|
||||
* @param name The setting's idetentifier
|
||||
* @param value Value of the setting
|
||||
* @param default_value Default of the setting if not present in qt_config
|
||||
* @param use_global Specifies if the custom or global config should be in use, for custom
|
||||
* configs
|
||||
*/
|
||||
void WriteSetting(const QString& name, const QVariant& value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
|
||||
bool use_global);
|
||||
|
||||
/**
|
||||
* Reads a value from the qt_config and applies it to the setting, using its label and default
|
||||
* value. If the config is a custom config, this will also read the global state of the setting
|
||||
* and apply that information to it.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting);
|
||||
|
||||
/**
|
||||
* Sets a value to the qt_config using the setting's label and default value. If the config is a
|
||||
* custom config, it will apply the global state, and the custom value if needed.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting);
|
||||
|
||||
/**
|
||||
* Reads a value from the qt_config using the setting's label and default value and applies the
|
||||
* value to the setting.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void ReadBasicSetting(Settings::Setting<Type, ranged>& setting);
|
||||
|
||||
/** Sets a value from the setting in the qt_config using the setting's label and default value.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void WriteBasicSetting(const Settings::Setting<Type, ranged>& setting);
|
||||
|
||||
ConfigType type;
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
std::string qt_config_loc;
|
||||
bool global;
|
||||
};
|
||||
|
95
src/citra_qt/configuration/configuration_shared.cpp
Normal file
95
src/citra_qt/configuration/configuration_shared.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_per_game.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting,
|
||||
const QCheckBox* checkbox,
|
||||
const CheckState& tracker) {
|
||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||
setting->SetValue(checkbox->checkState());
|
||||
} else if (!Settings::IsConfiguringGlobal()) {
|
||||
if (tracker == CheckState::Global) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(checkbox->checkState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
|
||||
const Settings::SwitchableSetting<bool>* setting) {
|
||||
if (setting->UsingGlobal()) {
|
||||
checkbox->setCheckState(Qt::PartiallyChecked);
|
||||
} else {
|
||||
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
|
||||
if (highlighted) {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
|
||||
.arg(widget->objectName()));
|
||||
} else {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
|
||||
.arg(widget->objectName()));
|
||||
}
|
||||
widget->show();
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
|
||||
const Settings::SwitchableSetting<bool>& setting,
|
||||
CheckState& tracker) {
|
||||
if (setting.UsingGlobal()) {
|
||||
tracker = CheckState::Global;
|
||||
} else {
|
||||
tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(setting.GetValue(true));
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
|
||||
bool global_state, CheckState& tracker) {
|
||||
if (global) {
|
||||
tracker = CheckState::Global;
|
||||
} else {
|
||||
tracker = (state == global_state) ? CheckState::On : CheckState::Off;
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(global_state);
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
|
||||
InsertGlobalItem(combobox, global);
|
||||
QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
|
||||
[target](int index) { SetHighlight(target, index != 0); });
|
||||
}
|
||||
|
||||
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
|
||||
const QString use_global_text =
|
||||
ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
|
||||
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
|
||||
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
|
||||
}
|
98
src/citra_qt/configuration/configuration_shared.h
Normal file
98
src/citra_qt/configuration/configuration_shared.h
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace ConfigurationShared {
|
||||
|
||||
constexpr int USE_GLOBAL_INDEX =
|
||||
0; ///< The index of the "Use global configuration" option in checkboxes
|
||||
constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
|
||||
constexpr int USE_GLOBAL_OFFSET = 2;
|
||||
|
||||
/// CheckBoxes require a tracker for their state since we emulate a tristate CheckBox
|
||||
enum class CheckState {
|
||||
Off, ///< Checkbox overrides to off/false
|
||||
On, ///< Checkbox overrides to on/true
|
||||
Global, ///< Checkbox defers to the global state
|
||||
Count, ///< Simply the number of states, not a valid checkbox state
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ApplyPerGameSetting given a setting and a Qt UI element, properly applies a Setting
|
||||
* taking into account the global/per-game check state. This is used for configuring checkboxes
|
||||
* @param setting
|
||||
* @param checkbox
|
||||
* @param tracker
|
||||
*/
|
||||
void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
|
||||
const CheckState& tracker);
|
||||
|
||||
/**
|
||||
* @brief ApplyPerGameSetting given a setting and a Qt UI element, properly applies a Setting
|
||||
* taking into account the global/per-game check state. This is used for both combo boxes
|
||||
* as well as any other widget that is accompanied by a combo box in per-game settings.
|
||||
* @param setting The setting class that stores the desired option
|
||||
* @param combobox The Qt combo box that stores the value/per-game status
|
||||
* @param transform A function that accepts the combo box index and transforms it to the
|
||||
* desired settings value. When used with sliders/edit the user can ignore the input value
|
||||
* and set a custom value this making this function useful for these widgets as well
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
|
||||
const QComboBox* combobox, auto transform) {
|
||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||
setting->SetValue(static_cast<Type>(transform(combobox->currentIndex())));
|
||||
} else if (!Settings::IsConfiguringGlobal()) {
|
||||
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(static_cast<Type>(
|
||||
transform(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simpler version of ApplyPerGameSetting without a transform parameter
|
||||
template <typename Type, bool ranged>
|
||||
void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
|
||||
const QComboBox* combobox) {
|
||||
const auto transform = [](s32 index) { return index; };
|
||||
return ApplyPerGameSetting(setting, combobox, transform);
|
||||
}
|
||||
|
||||
/// Sets a Qt UI element given a Settings::Setting
|
||||
void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::SwitchableSetting<Type, ranged>* setting) {
|
||||
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
|
||||
: static_cast<int>(setting->GetValue()) +
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
/// Given a Qt widget sets the background color to indicate whether the setting
|
||||
/// is per-game overriden (highlighted) or global (non-highlighted)
|
||||
void SetHighlight(QWidget* widget, bool highlighted);
|
||||
|
||||
/// Sets up a QCheckBox like a tristate one, given a Setting
|
||||
void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting,
|
||||
CheckState& tracker);
|
||||
void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
|
||||
CheckState& tracker);
|
||||
|
||||
/// Sets up coloring of a QWidget `target` based on the state of a QComboBox, and calls
|
||||
/// InsertGlobalItem
|
||||
void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
|
||||
|
||||
/// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
|
||||
void InsertGlobalItem(QComboBox* combobox, int global_index);
|
||||
|
||||
} // namespace ConfigurationShared
|
@ -9,10 +9,11 @@
|
||||
#endif
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_audio.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/mic.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_audio.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@ -31,25 +32,30 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
|
||||
}
|
||||
|
||||
ui->emulation_combo_box->addItem(tr("HLE (fast)"));
|
||||
ui->emulation_combo_box->addItem(tr("LLE (accurate)"));
|
||||
ui->emulation_combo_box->addItem(tr("LLE multi-core"));
|
||||
ui->emulation_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
const bool is_running = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->emulation_combo_box->setEnabled(!is_running);
|
||||
|
||||
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
||||
&ConfigureAudio::SetVolumeIndicatorText);
|
||||
|
||||
ui->input_device_combo_box->clear();
|
||||
ui->input_device_combo_box->addItem(tr("Default"));
|
||||
|
||||
#ifdef HAVE_CUBEB
|
||||
for (const auto& device : AudioCore::ListCubebInputDevices()) {
|
||||
ui->input_device_combo_box->addItem(QString::fromStdString(device));
|
||||
}
|
||||
#endif
|
||||
|
||||
connect(ui->input_type_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureAudio::UpdateAudioInputDevices);
|
||||
|
||||
ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
|
||||
ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
|
||||
|
||||
SetupPerGameUI();
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureAudio::UpdateAudioOutputDevices);
|
||||
}
|
||||
@ -61,27 +67,35 @@ void ConfigureAudio::SetConfiguration() {
|
||||
|
||||
// The device list cannot be pre-populated (nor listed) until the output sink is known.
|
||||
UpdateAudioOutputDevices(ui->output_sink_combo_box->currentIndex());
|
||||
|
||||
SetAudioDeviceFromDeviceID();
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
|
||||
ui->volume_slider->setValue(
|
||||
static_cast<int>(Settings::values.volume * ui->volume_slider->maximum()));
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
|
||||
|
||||
const s32 volume =
|
||||
static_cast<s32>(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
|
||||
ui->volume_slider->setValue(volume);
|
||||
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
|
||||
int selection;
|
||||
if (Settings::values.enable_dsp_lle) {
|
||||
if (Settings::values.enable_dsp_lle_multithread) {
|
||||
selection = 2;
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.volume.UsingGlobal()) {
|
||||
ui->volume_combo_box->setCurrentIndex(0);
|
||||
ui->volume_slider->setEnabled(false);
|
||||
} else {
|
||||
selection = 1;
|
||||
ui->volume_combo_box->setCurrentIndex(1);
|
||||
ui->volume_slider->setEnabled(true);
|
||||
}
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout,
|
||||
!Settings::values.volume.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->widget_emulation,
|
||||
!Settings::values.audio_emulation.UsingGlobal());
|
||||
ConfigurationShared::SetPerGameSetting(ui->emulation_combo_box,
|
||||
&Settings::values.audio_emulation);
|
||||
} else {
|
||||
selection = 0;
|
||||
}
|
||||
s32 selection = static_cast<s32>(Settings::values.audio_emulation.GetValue());
|
||||
ui->emulation_combo_box->setCurrentIndex(selection);
|
||||
}
|
||||
|
||||
int index = static_cast<int>(Settings::values.mic_input_type);
|
||||
s32 index = static_cast<s32>(Settings::values.mic_input_type.GetValue());
|
||||
ui->input_type_combo_box->setCurrentIndex(index);
|
||||
|
||||
UpdateAudioInputDevices(index);
|
||||
@ -90,7 +104,7 @@ void ConfigureAudio::SetConfiguration() {
|
||||
void ConfigureAudio::SetOutputSinkFromSinkID() {
|
||||
int new_sink_index = 0;
|
||||
|
||||
const QString sink_id = QString::fromStdString(Settings::values.sink_id);
|
||||
const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue());
|
||||
for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
|
||||
if (ui->output_sink_combo_box->itemText(index) == sink_id) {
|
||||
new_sink_index = index;
|
||||
@ -104,7 +118,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
|
||||
void ConfigureAudio::SetAudioDeviceFromDeviceID() {
|
||||
int new_device_index = -1;
|
||||
|
||||
const QString device_id = QString::fromStdString(Settings::values.audio_device_id);
|
||||
const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue());
|
||||
for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
|
||||
if (ui->audio_device_combo_box->itemText(index) == device_id) {
|
||||
new_device_index = index;
|
||||
@ -120,24 +134,31 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
|
||||
}
|
||||
|
||||
void ConfigureAudio::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
|
||||
ui->toggle_audio_stretching, audio_stretching);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.audio_emulation,
|
||||
ui->emulation_combo_box);
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.volume, ui->volume_combo_box, [this](s32) {
|
||||
return static_cast<float>(ui->volume_slider->value()) / ui->volume_slider->maximum();
|
||||
});
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
Settings::values.sink_id =
|
||||
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
|
||||
Settings::values.audio_device_id =
|
||||
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.volume =
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
|
||||
Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() != 0;
|
||||
Settings::values.enable_dsp_lle_multithread = ui->emulation_combo_box->currentIndex() == 2;
|
||||
Settings::values.mic_input_type =
|
||||
static_cast<Settings::MicInputType>(ui->input_type_combo_box->currentIndex());
|
||||
|
||||
if (ui->input_device_combo_box->currentIndex() == DEFAULT_INPUT_DEVICE_INDEX) {
|
||||
Settings::values.mic_input_device = Frontend::Mic::default_device_name;
|
||||
} else {
|
||||
Settings::values.mic_input_device = ui->input_device_combo_box->currentText().toStdString();
|
||||
Settings::values.mic_input_device =
|
||||
ui->input_device_combo_box->currentText().toStdString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,12 +178,41 @@ void ConfigureAudio::UpdateAudioInputDevices(int index) {
|
||||
AppleAuthorization::CheckAuthorizationForMicrophone();
|
||||
}
|
||||
#endif
|
||||
if (Settings::values.mic_input_device != Frontend::Mic::default_device_name) {
|
||||
if (Settings::values.mic_input_device.GetValue() != Frontend::Mic::default_device_name) {
|
||||
ui->input_device_combo_box->setCurrentText(
|
||||
QString::fromStdString(Settings::values.mic_input_device));
|
||||
QString::fromStdString(Settings::values.mic_input_device.GetValue()));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureAudio::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureAudio::SetupPerGameUI() {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
|
||||
return;
|
||||
}
|
||||
|
||||
ui->output_sink_combo_box->setVisible(false);
|
||||
ui->output_sink_label->setVisible(false);
|
||||
ui->audio_device_combo_box->setVisible(false);
|
||||
ui->audio_device_label->setVisible(false);
|
||||
ui->input_type_label->setVisible(false);
|
||||
ui->input_type_combo_box->setVisible(false);
|
||||
ui->input_device_label->setVisible(false);
|
||||
ui->input_device_combo_box->setVisible(false);
|
||||
ui->microphone_layout->setVisible(false);
|
||||
|
||||
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->volume_slider->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
|
||||
});
|
||||
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->emulation_combo_box, ui->widget_emulation,
|
||||
static_cast<u32>(Settings::values.audio_emulation.GetValue(true)));
|
||||
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->toggle_audio_stretching, Settings::values.enable_audio_stretching, audio_stretching);
|
||||
}
|
||||
|
@ -11,6 +11,10 @@ namespace Ui {
|
||||
class ConfigureAudio;
|
||||
}
|
||||
|
||||
namespace ConfigurationShared {
|
||||
enum class CheckState;
|
||||
}
|
||||
|
||||
class ConfigureAudio : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@ -30,5 +34,8 @@ private:
|
||||
void SetAudioDeviceFromDeviceID();
|
||||
void SetVolumeIndicatorText(int percentage);
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
ConfigurationShared::CheckState audio_stretching;
|
||||
std::unique_ptr<Ui::ConfigureAudio> ui;
|
||||
};
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>329</width>
|
||||
<height>344</height>
|
||||
<width>696</width>
|
||||
<height>527</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
@ -18,7 +18,17 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_emulation" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_emulation">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@ -30,14 +40,31 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="emulation_combo_box"/>
|
||||
<widget class="QComboBox" name="emulation_combo_box">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>HLE (fast)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LLE (accurate)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LLE multi-core</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<layout class="QHBoxLayout" name="output_engine_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label1">
|
||||
<widget class="QLabel" name="output_sink_label">
|
||||
<property name="text">
|
||||
<string>Output Engine</string>
|
||||
</property>
|
||||
@ -59,9 +86,9 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<layout class="QHBoxLayout" name="audio_device_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label2">
|
||||
<widget class="QLabel" name="audio_device_label">
|
||||
<property name="text">
|
||||
<string>Audio Device</string>
|
||||
</property>
|
||||
@ -73,12 +100,36 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="volume_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QComboBox" name="volume_combo_box">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global volume</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set volume:</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="volume_label">
|
||||
<property name="text">
|
||||
<string>Volume:</string>
|
||||
</property>
|
||||
@ -91,7 +142,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<width>30</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -109,7 +160,7 @@
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>10</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -133,12 +184,13 @@
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<widget class="QGroupBox" name="microphone_layout">
|
||||
<property name="title">
|
||||
<string>Microphone</string>
|
||||
</property>
|
||||
@ -146,7 +198,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QLabel" name="input_type_label">
|
||||
<property name="text">
|
||||
<string>Input Type</string>
|
||||
</property>
|
||||
@ -176,7 +228,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<widget class="QLabel" name="input_device_label">
|
||||
<property name="text">
|
||||
<string>Input Device</string>
|
||||
</property>
|
||||
|
@ -9,12 +9,10 @@
|
||||
#include <QMessageBox>
|
||||
#include <QWidget>
|
||||
#include "citra_qt/configuration/configure_camera.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "core/core.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/frontend/camera/factory.h"
|
||||
#include "core/frontend/camera/interface.h"
|
||||
#include "core/hle/service/cam/cam.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_camera.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@ -91,7 +89,7 @@ void ConfigureCamera::ConnectEvents() {
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked);
|
||||
connect(ui->preview_button, &QPushButton::clicked, this, [=] { StartPreviewing(); });
|
||||
connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); });
|
||||
connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
ui->camera_file->setDisabled(state == Qt::Checked);
|
||||
ui->toolButton->setDisabled(state == Qt::Checked);
|
||||
@ -99,12 +97,11 @@ void ConfigureCamera::ConnectEvents() {
|
||||
ui->camera_file->setText(QString{});
|
||||
}
|
||||
});
|
||||
connect(ui->camera_file, &QLineEdit::textChanged, this, [=] { StopPreviewing(); });
|
||||
connect(ui->system_camera,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
[=] { StopPreviewing(); });
|
||||
connect(ui->camera_flip, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, [=] { StopPreviewing(); });
|
||||
connect(ui->camera_file, &QLineEdit::textChanged, this, [this] { StopPreviewing(); });
|
||||
connect(ui->system_camera, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { StopPreviewing(); });
|
||||
connect(ui->camera_flip, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { StopPreviewing(); });
|
||||
}
|
||||
|
||||
void ConfigureCamera::UpdateCameraMode() {
|
||||
|
@ -3,17 +3,18 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QUrl>
|
||||
#include "citra_qt/configuration/configure_debug.h"
|
||||
#include "citra_qt/debugger/console.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "qcheckbox.h"
|
||||
#include "ui_configure_debug.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) {
|
||||
@ -24,19 +25,55 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
|
||||
connect(ui->toggle_renderer_debug, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) {
|
||||
try {
|
||||
Vulkan::Instance debug_inst{true};
|
||||
} catch (vk::LayerNotPresentError&) {
|
||||
ui->toggle_renderer_debug->toggle();
|
||||
QMessageBox::warning(this, tr("Validation layer not available"),
|
||||
tr("Unable to enable debug renderer because the layer "
|
||||
"<strong>VK_LAYER_KHRONOS_validation</strong> is missing. "
|
||||
"Please install the Vulkan SDK or the appropriate package "
|
||||
"of your distribution"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->toggle_dump_command_buffers, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) {
|
||||
try {
|
||||
Vulkan::Instance debug_inst{false, true};
|
||||
} catch (vk::LayerNotPresentError&) {
|
||||
ui->toggle_dump_command_buffers->toggle();
|
||||
QMessageBox::warning(this, tr("Command buffer dumping not available"),
|
||||
tr("Unable to enable command buffer dumping because the layer "
|
||||
"<strong>VK_LAYER_LUNARG_api_dump</strong> is missing. "
|
||||
"Please install the Vulkan SDK or the appropriate package "
|
||||
"of your distribution"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||
ui->toggle_dump_command_buffers->setEnabled(!is_powered_on);
|
||||
}
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() = default;
|
||||
|
||||
void ConfigureDebug::SetConfiguration() {
|
||||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
|
||||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
|
||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
|
||||
ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
|
||||
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
|
||||
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
@ -46,9 +83,11 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Log::SetGlobalFilter(filter);
|
||||
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
|
||||
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
|
||||
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureDebug::RetranslateUI() {
|
||||
|
@ -22,5 +22,6 @@ public:
|
||||
void RetranslateUI();
|
||||
void SetConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>443</width>
|
||||
<height>300</height>
|
||||
<width>454</width>
|
||||
<height>356</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -114,11 +114,31 @@
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_cpu_jit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable CPU JIT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_renderer_debug">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enables debug reporting in the currently selected graphics API. Causes measurable performance loss, don't enable unless for debugging purposes</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable debug renderer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_dump_command_buffers">
|
||||
<property name="text">
|
||||
<string>Dump command buffers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -7,11 +7,13 @@
|
||||
#include "citra_qt/configuration/config.h"
|
||||
#include "citra_qt/configuration/configure_dialog.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure.h"
|
||||
|
||||
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool enable_web_config)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureDialog>()), registry(registry) {
|
||||
Settings::SetConfiguringGlobal(true);
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->hotkeysTab->Populate(registry);
|
||||
ui->webTab->SetWebServiceConfigEnabled(enable_web_config);
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#include <QColorDialog>
|
||||
#include "citra_qt/configuration/configure_enhancements.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_enhancements.h"
|
||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
||||
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
|
||||
@ -21,7 +21,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
||||
|
||||
ui->layoutBox->setEnabled(!Settings::values.custom_layout);
|
||||
|
||||
ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer);
|
||||
ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer.GetValue());
|
||||
|
||||
connect(ui->render_3d_combobox,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
@ -50,26 +50,31 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
||||
}
|
||||
|
||||
void ConfigureEnhancements::SetConfiguration() {
|
||||
ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor);
|
||||
ui->render_3d_combobox->setCurrentIndex(static_cast<int>(Settings::values.render_3d));
|
||||
ui->factor_3d->setValue(Settings::values.factor_3d);
|
||||
updateShaders(Settings::values.render_3d);
|
||||
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode);
|
||||
ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor.GetValue());
|
||||
ui->render_3d_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.render_3d.GetValue()));
|
||||
ui->factor_3d->setValue(Settings::values.factor_3d.GetValue());
|
||||
ui->mono_rendering_eye->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.mono_render_option.GetValue()));
|
||||
updateShaders(Settings::values.render_3d.GetValue());
|
||||
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue());
|
||||
int tex_filter_idx = ui->texture_filter_combobox->findText(
|
||||
QString::fromStdString(Settings::values.texture_filter_name));
|
||||
QString::fromStdString(Settings::values.texture_filter_name.GetValue()));
|
||||
if (tex_filter_idx == -1) {
|
||||
ui->texture_filter_combobox->setCurrentIndex(0);
|
||||
} else {
|
||||
ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx);
|
||||
}
|
||||
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
|
||||
ui->swap_screen->setChecked(Settings::values.swap_screen);
|
||||
ui->upright_screen->setChecked(Settings::values.upright_screen);
|
||||
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures);
|
||||
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures);
|
||||
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures);
|
||||
bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue);
|
||||
ui->layout_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.layout_option.GetValue()));
|
||||
ui->swap_screen->setChecked(Settings::values.swap_screen.GetValue());
|
||||
ui->upright_screen->setChecked(Settings::values.upright_screen.GetValue());
|
||||
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue());
|
||||
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
|
||||
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue());
|
||||
bg_color =
|
||||
QColor::fromRgbF(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue());
|
||||
QPixmap pixmap(ui->bg_button->size());
|
||||
pixmap.fill(bg_color);
|
||||
const QIcon color_icon(pixmap);
|
||||
@ -92,7 +97,7 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op
|
||||
for (const auto& shader : OpenGL::GetPostProcessingShaderList(
|
||||
stereo_option == Settings::StereoRenderOption::Anaglyph)) {
|
||||
ui->shader_combobox->addItem(QString::fromStdString(shader));
|
||||
if (Settings::values.pp_shader_name == shader)
|
||||
if (Settings::values.pp_shader_name.GetValue() == shader)
|
||||
ui->shader_combobox->setCurrentIndex(ui->shader_combobox->count() - 1);
|
||||
}
|
||||
}
|
||||
@ -107,6 +112,8 @@ void ConfigureEnhancements::ApplyConfiguration() {
|
||||
Settings::values.render_3d =
|
||||
static_cast<Settings::StereoRenderOption>(ui->render_3d_combobox->currentIndex());
|
||||
Settings::values.factor_3d = ui->factor_3d->value();
|
||||
Settings::values.mono_render_option =
|
||||
static_cast<Settings::MonoRenderOption>(ui->mono_rendering_eye->currentIndex());
|
||||
Settings::values.pp_shader_name =
|
||||
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
|
||||
Settings::values.filter_mode = ui->toggle_linear_filter->isChecked();
|
||||
|
@ -6,9 +6,10 @@
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Settings {
|
||||
enum class StereoRenderOption;
|
||||
enum class StereoRenderOption : u32;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
|
@ -207,6 +207,31 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Eye to Render in Monoscopic Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="mono_rendering_eye">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Left Eye (default)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Right Eye</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -247,6 +272,11 @@
|
||||
<string>Side by Side</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Separate Windows</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -350,6 +380,7 @@
|
||||
<tabstop>texture_filter_combobox</tabstop>
|
||||
<tabstop>render_3d_combobox</tabstop>
|
||||
<tabstop>factor_3d</tabstop>
|
||||
<tabstop>mono_rendering_eye</tabstop>
|
||||
<tabstop>layout_combobox</tabstop>
|
||||
<tabstop>swap_screen</tabstop>
|
||||
<tabstop>upright_screen</tabstop>
|
||||
|
@ -6,10 +6,11 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QUrl>
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_general.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_general.h"
|
||||
|
||||
// The QSlider doesn't have an easy way to set a custom step amount,
|
||||
@ -31,14 +32,17 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
// Set a minimum width for the label to prevent the slider from changing size.
|
||||
// This scales across DPIs, and is acceptable for uncapitalized strings.
|
||||
ui->emulation_speed_display_label->setMinimumWidth(tr("unthrottled").size() * 6);
|
||||
ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||
ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||
|
||||
SetupPerGameUI();
|
||||
SetConfiguration();
|
||||
|
||||
ui->updateBox->setVisible(UISettings::values.updater_found);
|
||||
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
||||
&ConfigureGeneral::ResetDefaults);
|
||||
|
||||
connect(ui->frame_limit, &QSlider::valueChanged, [&](int value) {
|
||||
connect(ui->frame_limit, &QSlider::valueChanged, this, [&](int value) {
|
||||
if (value == ui->frame_limit->maximum()) {
|
||||
ui->emulation_speed_display_label->setText(tr("unthrottled"));
|
||||
} else {
|
||||
@ -49,17 +53,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->frame_limit_alternate, &QSlider::valueChanged, [&](int value) {
|
||||
if (value == ui->frame_limit_alternate->maximum()) {
|
||||
ui->emulation_speed_alternate_display_label->setText(tr("unthrottled"));
|
||||
} else {
|
||||
ui->emulation_speed_alternate_display_label->setText(
|
||||
QStringLiteral("%1%")
|
||||
.arg(SliderToSettings(value))
|
||||
.rightJustified(tr("unthrottled").size()));
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->change_screenshot_dir, &QToolButton::clicked, this, [this] {
|
||||
const QString dir_path = QFileDialog::getExistingDirectory(
|
||||
this, tr("Select Screenshot Directory"), ui->screenshot_dir_path->text(),
|
||||
@ -73,20 +66,21 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
|
||||
void ConfigureGeneral::SetConfiguration() {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
|
||||
ui->toggle_background_pause->setChecked(
|
||||
UISettings::values.pause_when_in_background.GetValue());
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
|
||||
|
||||
ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start);
|
||||
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close);
|
||||
ui->toggle_update_check->setChecked(
|
||||
UISettings::values.check_for_update_on_start.GetValue());
|
||||
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close.GetValue());
|
||||
}
|
||||
|
||||
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
|
||||
ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
|
||||
|
||||
if (Settings::values.frame_limit == 0) {
|
||||
if (Settings::values.frame_limit.GetValue() == 0) {
|
||||
ui->frame_limit->setValue(ui->frame_limit->maximum());
|
||||
} else {
|
||||
ui->frame_limit->setValue(SettingsToSlider(Settings::values.frame_limit));
|
||||
ui->frame_limit->setValue(SettingsToSlider(Settings::values.frame_limit.GetValue()));
|
||||
}
|
||||
if (ui->frame_limit->value() == ui->frame_limit->maximum()) {
|
||||
ui->emulation_speed_display_label->setText(tr("unthrottled"));
|
||||
@ -97,32 +91,48 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
.rightJustified(tr("unthrottled").size()));
|
||||
}
|
||||
|
||||
ui->toggle_alternate_speed->setChecked(Settings::values.use_frame_limit_alternate);
|
||||
|
||||
if (Settings::values.frame_limit_alternate == 0) {
|
||||
ui->frame_limit_alternate->setValue(ui->frame_limit_alternate->maximum());
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.frame_limit.UsingGlobal()) {
|
||||
ui->emulation_speed_combo->setCurrentIndex(0);
|
||||
ui->frame_limit->setEnabled(false);
|
||||
} else {
|
||||
ui->frame_limit_alternate->setValue(
|
||||
SettingsToSlider(Settings::values.frame_limit_alternate));
|
||||
ui->emulation_speed_combo->setCurrentIndex(1);
|
||||
ui->frame_limit->setEnabled(true);
|
||||
}
|
||||
if (ui->frame_limit_alternate->value() == ui->frame_limit_alternate->maximum()) {
|
||||
ui->emulation_speed_alternate_display_label->setText(tr("unthrottled"));
|
||||
if (UISettings::values.screenshot_path.UsingGlobal()) {
|
||||
ui->screenshot_combo->setCurrentIndex(0);
|
||||
ui->screenshot_dir_path->setEnabled(false);
|
||||
ui->change_screenshot_dir->setEnabled(false);
|
||||
} else {
|
||||
ui->emulation_speed_alternate_display_label->setText(
|
||||
QStringLiteral("%1%")
|
||||
.arg(SliderToSettings(ui->frame_limit_alternate->value()))
|
||||
.rightJustified(tr("unthrottled").size()));
|
||||
ui->screenshot_combo->setCurrentIndex(1);
|
||||
ui->screenshot_dir_path->setEnabled(true);
|
||||
ui->change_screenshot_dir->setEnabled(true);
|
||||
}
|
||||
ConfigurationShared::SetHighlight(ui->widget_screenshot,
|
||||
!UISettings::values.screenshot_path.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->emulation_speed_layout,
|
||||
!Settings::values.frame_limit.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->widget_region,
|
||||
!Settings::values.region_value.UsingGlobal());
|
||||
const bool is_region_global = Settings::values.region_value.UsingGlobal();
|
||||
ui->region_combobox->setCurrentIndex(
|
||||
is_region_global ? ConfigurationShared::USE_GLOBAL_INDEX
|
||||
: static_cast<int>(Settings::values.region_value.GetValue()) +
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET + 1);
|
||||
} else {
|
||||
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
|
||||
ui->region_combobox->setCurrentIndex(Settings::values.region_value.GetValue() + 1);
|
||||
}
|
||||
|
||||
QString screenshot_path = UISettings::values.screenshot_path;
|
||||
if (screenshot_path.isEmpty()) {
|
||||
screenshot_path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
|
||||
screenshot_path.append(QStringLiteral("screenshots/"));
|
||||
FileUtil::CreateFullPath(screenshot_path.toStdString());
|
||||
UISettings::values.screenshot_path.SetGlobal(ui->screenshot_combo->currentIndex() ==
|
||||
ConfigurationShared::USE_GLOBAL_INDEX);
|
||||
std::string screenshot_path = UISettings::values.screenshot_path.GetValue();
|
||||
if (screenshot_path.empty()) {
|
||||
screenshot_path = FileUtil::GetUserPath(FileUtil::UserPath::UserDir) + "screenshots/";
|
||||
FileUtil::CreateFullPath(screenshot_path);
|
||||
UISettings::values.screenshot_path = screenshot_path;
|
||||
}
|
||||
ui->screenshot_dir_path->setText(screenshot_path);
|
||||
ui->screenshot_dir_path->setText(QString::fromStdString(screenshot_path));
|
||||
}
|
||||
|
||||
void ConfigureGeneral::ResetDefaults() {
|
||||
@ -131,39 +141,67 @@ void ConfigureGeneral::ResetDefaults() {
|
||||
tr("Are you sure you want to <b>reset your settings</b> and close Citra?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (answer == QMessageBox::No)
|
||||
if (answer == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini");
|
||||
FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "custom");
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_value, ui->region_combobox,
|
||||
[](s32 index) { return index - 1; });
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.frame_limit, ui->emulation_speed_combo, [this](s32) {
|
||||
const bool is_maximum = ui->frame_limit->value() == ui->frame_limit->maximum();
|
||||
return is_maximum ? 0 : SliderToSettings(ui->frame_limit->value());
|
||||
});
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&UISettings::values.screenshot_path, ui->screenshot_combo,
|
||||
[this](s32) { return ui->screenshot_dir_path->text().toStdString(); });
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
|
||||
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
|
||||
|
||||
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
|
||||
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
|
||||
|
||||
UISettings::values.screenshot_path = ui->screenshot_dir_path->text();
|
||||
|
||||
Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
|
||||
|
||||
if (ui->frame_limit->value() == ui->frame_limit->maximum()) {
|
||||
Settings::values.frame_limit = 0;
|
||||
} else {
|
||||
Settings::values.frame_limit = SliderToSettings(ui->frame_limit->value());
|
||||
}
|
||||
Settings::values.use_frame_limit_alternate = ui->toggle_alternate_speed->isChecked();
|
||||
if (ui->frame_limit_alternate->value() == ui->frame_limit_alternate->maximum()) {
|
||||
Settings::values.frame_limit_alternate = 0;
|
||||
} else {
|
||||
Settings::values.frame_limit_alternate =
|
||||
SliderToSettings(ui->frame_limit_alternate->value());
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGeneral::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::SetupPerGameUI() {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->region_combobox->setEnabled(Settings::values.region_value.UsingGlobal());
|
||||
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ui->emulation_speed_combo, qOverload<int>(&QComboBox::activated), this,
|
||||
[this](int index) {
|
||||
ui->frame_limit->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->emulation_speed_layout, index == 1);
|
||||
});
|
||||
|
||||
connect(ui->screenshot_combo, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->screenshot_dir_path->setEnabled(index == 1);
|
||||
ui->change_screenshot_dir->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->widget_screenshot, index == 1);
|
||||
});
|
||||
|
||||
ui->general_group->setVisible(false);
|
||||
ui->updateBox->setVisible(false);
|
||||
ui->button_reset_defaults->setVisible(false);
|
||||
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->region_combobox, ui->widget_region,
|
||||
static_cast<u32>(Settings::values.region_value.GetValue(true) + 1));
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ public:
|
||||
void RetranslateUI();
|
||||
void SetConfiguration();
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
};
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>408</width>
|
||||
<height>436</height>
|
||||
<width>524</width>
|
||||
<height>578</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -17,7 +17,7 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<widget class="QGroupBox" name="general_group">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
@ -70,12 +70,34 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<widget class="QGroupBox" name="emulation_group">
|
||||
<property name="title">
|
||||
<string>Emulation</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_region" native="true">
|
||||
<layout class="QHBoxLayout" name="region_layout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="region_label">
|
||||
<property name="text">
|
||||
<string>Region:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="region_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
@ -119,21 +141,46 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="emulation_speed_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="emulation_speed_layout_inner">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="emulation_speed_combo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global emulation speed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set emulation speed:</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_emulation_speed">
|
||||
<property name="text">
|
||||
<string>Emulation Speed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="toggle_alternate_speed">
|
||||
<property name="text">
|
||||
<string>Use Alternate Speed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<item>
|
||||
<widget class="QSlider" name="frame_limit">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
@ -158,7 +205,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item>
|
||||
<widget class="QLabel" name="emulation_speed_display_label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
@ -168,48 +215,9 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Region:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QSlider" name="frame_limit_alternate">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>199</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>39</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="emulation_speed_alternate_display_label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -218,17 +226,48 @@
|
||||
<property name="title">
|
||||
<string>Screenshots</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QWidget" name="widget_screenshot" native="true">
|
||||
<layout class="QHBoxLayout" name="screenshot_layout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QComboBox" name="screenshot_combo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global screenshot path</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set screenshot path:</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="screenshot_dir_label">
|
||||
<property name="text">
|
||||
<string>Save Screenshots To</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="screenshot_dir_path">
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="screenshot_dir_path"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="change_screenshot_dir">
|
||||
@ -240,6 +279,9 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignRight">
|
||||
<widget class="QPushButton" name="button_reset_defaults">
|
||||
<property name="text">
|
||||
@ -270,12 +312,6 @@
|
||||
<tabstop>toggle_hide_mouse</tabstop>
|
||||
<tabstop>toggle_update_check</tabstop>
|
||||
<tabstop>toggle_auto_update</tabstop>
|
||||
<tabstop>region_combobox</tabstop>
|
||||
<tabstop>frame_limit</tabstop>
|
||||
<tabstop>toggle_alternate_speed</tabstop>
|
||||
<tabstop>frame_limit_alternate</tabstop>
|
||||
<tabstop>screenshot_dir_path</tabstop>
|
||||
<tabstop>change_screenshot_dir</tabstop>
|
||||
<tabstop>button_reset_defaults</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
@ -6,22 +6,37 @@
|
||||
#ifdef __APPLE__
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_graphics.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
DiscoverPhysicalDevices();
|
||||
SetupPerGameUI();
|
||||
SetConfiguration();
|
||||
|
||||
ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked());
|
||||
ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
const bool not_running = !Core::System::GetInstance().IsPoweredOn();
|
||||
const bool hw_renderer_enabled = ui->toggle_hw_renderer->isChecked();
|
||||
ui->toggle_hw_renderer->setEnabled(not_running);
|
||||
ui->hw_renderer_group->setEnabled(hw_renderer_enabled && not_running);
|
||||
ui->toggle_vsync_new->setEnabled(not_running);
|
||||
ui->graphics_api_combo->setEnabled(not_running);
|
||||
ui->toggle_shader_jit->setEnabled(not_running);
|
||||
ui->toggle_disk_shader_cache->setEnabled(hw_renderer_enabled && not_running);
|
||||
ui->physical_device_combo->setEnabled(not_running);
|
||||
SetPhysicalDeviceComboVisibility(ui->graphics_api_combo->currentIndex());
|
||||
|
||||
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGraphics::SetPhysicalDeviceComboVisibility);
|
||||
|
||||
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] {
|
||||
auto checked = ui->toggle_hw_renderer->isChecked();
|
||||
const bool checked = ui->toggle_hw_renderer->isChecked();
|
||||
ui->hw_renderer_group->setEnabled(checked);
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked && ui->toggle_hw_shader->isChecked());
|
||||
});
|
||||
@ -31,7 +46,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
ui->toggle_hw_shader->isChecked());
|
||||
|
||||
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
|
||||
auto checked = ui->toggle_hw_shader->isChecked();
|
||||
const bool checked = ui->toggle_hw_shader->isChecked();
|
||||
ui->hw_shader_group->setEnabled(checked);
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked);
|
||||
});
|
||||
@ -62,25 +77,100 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
ConfigureGraphics::~ConfigureGraphics() = default;
|
||||
|
||||
void ConfigureGraphics::SetConfiguration() {
|
||||
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer);
|
||||
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader);
|
||||
ui->toggle_separable_shader->setChecked(Settings::values.separable_shader);
|
||||
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul);
|
||||
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
|
||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
|
||||
ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer.GetValue());
|
||||
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
|
||||
ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue());
|
||||
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue());
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
|
||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
|
||||
ui->graphics_api_combo->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.graphics_api.GetValue()));
|
||||
ui->physical_device_combo->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.physical_device.GetValue()));
|
||||
ui->toggle_async_recording->setChecked(Settings::values.async_command_recording.GetValue());
|
||||
ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen.GetValue());
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.use_hw_renderer = ui->toggle_hw_renderer->isChecked();
|
||||
Settings::values.use_hw_shader = ui->toggle_hw_shader->isChecked();
|
||||
Settings::values.separable_shader = ui->toggle_separable_shader->isChecked();
|
||||
Settings::values.shaders_accurate_mul = ui->toggle_accurate_mul->isChecked();
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_renderer,
|
||||
ui->toggle_hw_renderer, use_hw_renderer);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader,
|
||||
use_hw_shader);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader,
|
||||
ui->toggle_separable_shader, separable_shader);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul,
|
||||
ui->toggle_accurate_mul, shaders_accurate_mul);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
|
||||
ui->toggle_disk_shader_cache, use_disk_shader_cache);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync_new, ui->toggle_vsync_new,
|
||||
use_vsync_new);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api,
|
||||
ui->graphics_api_combo);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.physical_device,
|
||||
ui->physical_device_combo);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_command_recording,
|
||||
ui->toggle_async_recording, async_command_recording);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.spirv_shader_gen,
|
||||
ui->spirv_shader_gen, spirv_shader_gen);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
|
||||
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
|
||||
Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::SetupPerGameUI() {
|
||||
// Block the global settings if a game is currently running that overrides them
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->toggle_hw_renderer->setEnabled(Settings::values.use_hw_renderer.UsingGlobal());
|
||||
ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal());
|
||||
ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal());
|
||||
ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal());
|
||||
ui->toggle_disk_shader_cache->setEnabled(
|
||||
Settings::values.use_disk_shader_cache.UsingGlobal());
|
||||
ui->toggle_vsync_new->setEnabled(Settings::values.use_vsync_new.UsingGlobal());
|
||||
return;
|
||||
}
|
||||
|
||||
ui->toggle_shader_jit->setVisible(false);
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_hw_renderer,
|
||||
Settings::values.use_hw_renderer, use_hw_renderer);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader,
|
||||
use_hw_shader);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader,
|
||||
Settings::values.separable_shader, separable_shader);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->toggle_accurate_mul, Settings::values.shaders_accurate_mul, shaders_accurate_mul);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_disk_shader_cache,
|
||||
Settings::values.use_disk_shader_cache,
|
||||
use_disk_shader_cache);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new,
|
||||
use_vsync_new);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::DiscoverPhysicalDevices() {
|
||||
Vulkan::Instance instance{};
|
||||
const auto physical_devices = instance.GetPhysicalDevices();
|
||||
|
||||
ui->physical_device_combo->clear();
|
||||
for (const vk::PhysicalDevice& physical_device : physical_devices) {
|
||||
const QString name = QString::fromLocal8Bit(physical_device.getProperties().deviceName);
|
||||
ui->physical_device_combo->addItem(name);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
|
||||
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
|
||||
const bool is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
|
||||
ui->physical_device_label->setVisible(is_visible);
|
||||
ui->physical_device_combo->setVisible(is_visible);
|
||||
ui->spirv_shader_gen->setVisible(is_visible);
|
||||
}
|
||||
|
@ -11,6 +11,10 @@ namespace Ui {
|
||||
class ConfigureGraphics;
|
||||
}
|
||||
|
||||
namespace ConfigurationShared {
|
||||
enum class CheckState;
|
||||
}
|
||||
|
||||
class ConfigureGraphics : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@ -24,6 +28,20 @@ public:
|
||||
|
||||
void UpdateBackgroundColorButton(const QColor& color);
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
private:
|
||||
void DiscoverPhysicalDevices();
|
||||
void SetPhysicalDeviceComboVisibility(int index);
|
||||
|
||||
ConfigurationShared::CheckState use_hw_renderer;
|
||||
ConfigurationShared::CheckState use_hw_shader;
|
||||
ConfigurationShared::CheckState separable_shader;
|
||||
ConfigurationShared::CheckState shaders_accurate_mul;
|
||||
ConfigurationShared::CheckState use_disk_shader_cache;
|
||||
ConfigurationShared::CheckState use_vsync_new;
|
||||
ConfigurationShared::CheckState async_command_recording;
|
||||
ConfigurationShared::CheckState spirv_shader_gen;
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
QColor bg_color;
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>430</height>
|
||||
<height>513</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
@ -20,6 +20,66 @@
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="apiBox">
|
||||
<property name="title">
|
||||
<string>API Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="graphics_api_label">
|
||||
<property name="text">
|
||||
<string>Graphics API</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="graphics_api_combo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OpenGL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OpenGLES</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vulkan</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="physical_device_label">
|
||||
<property name="text">
|
||||
<string>Physical device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="physical_device_combo"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="spirv_shader_gen">
|
||||
<property name="text">
|
||||
<string>SPIR-V Shader Generation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="rendererBox">
|
||||
<property name="title">
|
||||
@ -118,6 +178,16 @@
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_async_recording">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Offloads command buffer recording and fragment shader generation to a worker thread. Can improve performance especially on weaker systems. Disable if you notice better performance. If unsure leave it enabled,</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Async Command Recording</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_disk_shader_cache">
|
||||
<property name="toolTip">
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "citra_qt/configuration/configure_hotkeys.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "citra_qt/util/sequence_dialog/sequence_dialog.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_hotkeys.h"
|
||||
|
||||
ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
|
||||
|
@ -192,11 +192,24 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
if (!button_map[button_id])
|
||||
continue;
|
||||
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(button_map[button_id], &QPushButton::clicked, [=]() {
|
||||
connect(button_map[button_id], &QPushButton::clicked, [this, button_id]() {
|
||||
HandleClick(
|
||||
button_map[button_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
buttons_param[button_id] = params;
|
||||
[this, button_id](Common::ParamPackage params) {
|
||||
// Workaround for ZL & ZR for analog triggers like on XBOX controllors.
|
||||
// Analog triggers (from controllers like the XBOX controller) would not
|
||||
// work due to a different range of their signals (from 0 to 255 on
|
||||
// analog triggers instead of -32768 to 32768 on analog joysticks). The
|
||||
// SDL driver misinterprets analog triggers as analog joysticks.
|
||||
// TODO: reinterpret the signal range for analog triggers to map the
|
||||
// values correctly. This is required for the correct emulation of the
|
||||
// analog triggers of the GameCube controller.
|
||||
if (button_id == Settings::NativeButton::ZL ||
|
||||
button_id == Settings::NativeButton::ZR) {
|
||||
params.Set("direction", "+");
|
||||
params.Set("threshold", "0.5");
|
||||
}
|
||||
buttons_param[button_id] = std::move(params);
|
||||
// If the user closes the dialog, the changes are reverted in
|
||||
// `GMainWindow::OnConfigure()`
|
||||
ApplyConfiguration();
|
||||
@ -204,16 +217,16 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(button_map[button_id], &QPushButton::customContextMenuRequested,
|
||||
[=](const QPoint& menu_location) {
|
||||
connect(button_map[button_id], &QPushButton::customContextMenuRequested, this,
|
||||
[this, button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
@ -230,10 +243,11 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
continue;
|
||||
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
|
||||
Qt::CustomContextMenu);
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() {
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, this,
|
||||
[this, analog_id, sub_button_id]() {
|
||||
HandleClick(
|
||||
analog_map_buttons[analog_id][sub_button_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
[this, analog_id, sub_button_id](const Common::ParamPackage& params) {
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
ApplyConfiguration();
|
||||
@ -242,15 +256,16 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(analog_map_buttons[analog_id][sub_button_id],
|
||||
&QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
|
||||
&QPushButton::customContextMenuRequested, this,
|
||||
[this, analog_id, sub_button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
Config::default_analogs[analog_id][sub_button_id])};
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
@ -264,7 +279,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
menu_location));
|
||||
});
|
||||
}
|
||||
connect(analog_map_stick[analog_id], &QPushButton::clicked, [=]() {
|
||||
connect(analog_map_stick[analog_id], &QPushButton::clicked, this, [this, analog_id]() {
|
||||
if (QMessageBox::information(
|
||||
this, tr("Information"),
|
||||
tr("After pressing OK, first move your joystick horizontally, "
|
||||
@ -272,7 +287,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
|
||||
HandleClick(
|
||||
analog_map_stick[analog_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
[this, analog_id](const Common::ParamPackage& params) {
|
||||
analogs_param[analog_id] = params;
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
@ -280,8 +295,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
InputCommon::Polling::DeviceType::Analog);
|
||||
}
|
||||
});
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
|
||||
const int slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, this,
|
||||
[this, analog_id] {
|
||||
const int slider_value =
|
||||
analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
const auto engine = analogs_param[analog_id].Get("engine", "");
|
||||
if (engine == "sdl" || engine == "gcpad") {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
@ -299,10 +316,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
|
||||
// The Circle Mod button is common for both the sticks, so update the modifier settings
|
||||
// for both the sticks.
|
||||
connect(ui->buttonCircleMod, &QPushButton::clicked, [=]() {
|
||||
connect(ui->buttonCircleMod, &QPushButton::clicked, this, [this]() {
|
||||
HandleClick(
|
||||
ui->buttonCircleMod,
|
||||
[=](const Common::ParamPackage& params) {
|
||||
[this](const Common::ParamPackage& params) {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
SetAnalogButton(params, analogs_param[analog_id], "modifier");
|
||||
@ -312,10 +329,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(ui->buttonCircleMod, &QPushButton::customContextMenuRequested,
|
||||
connect(ui->buttonCircleMod, &QPushButton::customContextMenuRequested, this,
|
||||
[&](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
analogs_param[analog_id].Erase("modifier");
|
||||
@ -325,7 +342,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
});
|
||||
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
@ -341,7 +358,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
context_menu.exec(ui->buttonCircleMod->mapToGlobal(menu_location));
|
||||
});
|
||||
|
||||
connect(ui->buttonMotionTouch, &QPushButton::clicked, [this] {
|
||||
connect(ui->buttonMotionTouch, &QPushButton::clicked, this, [this] {
|
||||
QDialog* motion_touch_dialog = new ConfigureMotionTouch(this);
|
||||
return motion_touch_dialog->exec();
|
||||
});
|
||||
@ -356,8 +373,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureInput::DeleteProfile);
|
||||
connect(ui->buttonRename, &QPushButton::clicked, this, &ConfigureInput::RenameProfile);
|
||||
|
||||
connect(ui->profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
[this](int i) {
|
||||
connect(ui->profile, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int i) {
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
||||
Settings::LoadProfile(i);
|
||||
@ -365,9 +381,9 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
});
|
||||
|
||||
timeout_timer->setSingleShot(true);
|
||||
connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
|
||||
connect(timeout_timer.get(), &QTimer::timeout, this, [this]() { SetPollingResult({}, true); });
|
||||
|
||||
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
||||
connect(poll_timer.get(), &QTimer::timeout, this, [this]() {
|
||||
Common::ParamPackage params;
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
@ -554,7 +570,7 @@ void ConfigureInput::AutoMap() {
|
||||
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
|
||||
return;
|
||||
}
|
||||
input_setter = [=](const Common::ParamPackage& params) {
|
||||
input_setter = [this](const Common::ParamPackage& params) {
|
||||
MapFromButton(params);
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <QKeySequence>
|
||||
#include <QWidget>
|
||||
#include "common/param_package.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
class QKeyEvent;
|
||||
|
@ -23,8 +23,9 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
status_label = new QLabel(tr("Communicating with the server..."));
|
||||
cancel_button = new QPushButton(tr("Cancel"));
|
||||
connect(cancel_button, &QPushButton::clicked, this, [this] {
|
||||
if (!completed)
|
||||
if (!completed) {
|
||||
job->Stop();
|
||||
}
|
||||
accept();
|
||||
});
|
||||
layout->addWidget(status_label);
|
||||
@ -46,6 +47,9 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
case CalibrationConfigurationJob::Status::Completed:
|
||||
text = tr("Configuration completed!");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unknown calibration status {}", status);
|
||||
break;
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text));
|
||||
if (status == CalibrationConfigurationJob::Status::Completed) {
|
||||
@ -63,31 +67,33 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
|
||||
CalibrationConfigurationDialog::~CalibrationConfigurationDialog() = default;
|
||||
|
||||
void CalibrationConfigurationDialog::UpdateLabelText(QString text) {
|
||||
void CalibrationConfigurationDialog::UpdateLabelText(const QString& text) {
|
||||
status_label->setText(text);
|
||||
}
|
||||
|
||||
void CalibrationConfigurationDialog::UpdateButtonText(QString text) {
|
||||
void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) {
|
||||
cancel_button->setText(text);
|
||||
}
|
||||
|
||||
const std::array<std::pair<const char*, const char*>, 3> MotionProviders = {
|
||||
{{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
|
||||
constexpr std::array<std::pair<const char*, const char*>, 3> MotionProviders = {{
|
||||
{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
|
||||
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
|
||||
{"sdl", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "SDL")}}};
|
||||
{"sdl", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "SDL")},
|
||||
}};
|
||||
|
||||
const std::array<std::pair<const char*, const char*>, 2> TouchProviders = {
|
||||
{{"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
|
||||
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}}};
|
||||
constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{
|
||||
{"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
|
||||
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
|
||||
}};
|
||||
|
||||
ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureMotionTouch>()),
|
||||
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
|
||||
ui->setupUi(this);
|
||||
for (auto [provider, name] : MotionProviders) {
|
||||
for (const auto& [provider, name] : MotionProviders) {
|
||||
ui->motion_provider->addItem(tr(name), QString::fromUtf8(provider));
|
||||
}
|
||||
for (auto [provider, name] : TouchProviders) {
|
||||
for (const auto& [provider, name] : TouchProviders) {
|
||||
ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider));
|
||||
}
|
||||
|
||||
@ -99,9 +105,9 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
||||
"style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
|
||||
|
||||
timeout_timer->setSingleShot(true);
|
||||
connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
|
||||
connect(timeout_timer.get(), &QTimer::timeout, this, [this]() { SetPollingResult({}, true); });
|
||||
|
||||
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
||||
connect(poll_timer.get(), &QTimer::timeout, this, [this]() {
|
||||
Common::ParamPackage params;
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
@ -122,10 +128,10 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
||||
ConfigureMotionTouch::~ConfigureMotionTouch() = default;
|
||||
|
||||
void ConfigureMotionTouch::SetConfiguration() {
|
||||
Common::ParamPackage motion_param(Settings::values.current_input_profile.motion_device);
|
||||
Common::ParamPackage touch_param(Settings::values.current_input_profile.touch_device);
|
||||
std::string motion_engine = motion_param.Get("engine", "motion_emu");
|
||||
std::string touch_engine = touch_param.Get("engine", "emu_window");
|
||||
const Common::ParamPackage motion_param(Settings::values.current_input_profile.motion_device);
|
||||
const Common::ParamPackage touch_param(Settings::values.current_input_profile.touch_device);
|
||||
const std::string motion_engine = motion_param.Get("engine", "motion_emu");
|
||||
const std::string touch_engine = touch_param.Get("engine", "emu_window");
|
||||
|
||||
ui->motion_provider->setCurrentIndex(
|
||||
ui->motion_provider->findData(QString::fromStdString(motion_engine)));
|
||||
@ -156,8 +162,8 @@ void ConfigureMotionTouch::SetConfiguration() {
|
||||
}
|
||||
|
||||
void ConfigureMotionTouch::UpdateUiDisplay() {
|
||||
std::string motion_engine = ui->motion_provider->currentData().toString().toStdString();
|
||||
std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
|
||||
const std::string motion_engine = ui->motion_provider->currentData().toString().toStdString();
|
||||
const std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
|
||||
|
||||
if (motion_engine == "motion_emu") {
|
||||
ui->motion_sensitivity_label->setVisible(true);
|
||||
@ -179,9 +185,8 @@ void ConfigureMotionTouch::UpdateUiDisplay() {
|
||||
ui->touch_calibration->setVisible(true);
|
||||
ui->touch_calibration_config->setVisible(true);
|
||||
ui->touch_calibration_label->setVisible(true);
|
||||
ui->touch_calibration->setText(QStringLiteral("(%1, %2) - (%3, %4)")
|
||||
.arg(QString::number(min_x), QString::number(min_y),
|
||||
QString::number(max_x), QString::number(max_y)));
|
||||
ui->touch_calibration->setText(
|
||||
QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y));
|
||||
} else {
|
||||
ui->touch_calibration->setVisible(false);
|
||||
ui->touch_calibration_config->setVisible(false);
|
||||
@ -196,13 +201,11 @@ void ConfigureMotionTouch::UpdateUiDisplay() {
|
||||
}
|
||||
|
||||
void ConfigureMotionTouch::ConnectEvents() {
|
||||
connect(ui->motion_provider,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->motion_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this]([[maybe_unused]] int index) { UpdateUiDisplay(); });
|
||||
connect(ui->touch_provider,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this]([[maybe_unused]] int index) { UpdateUiDisplay(); });
|
||||
connect(ui->motion_controller_button, &QPushButton::clicked, [=]() {
|
||||
connect(ui->motion_controller_button, &QPushButton::clicked, this, [this]() {
|
||||
if (QMessageBox::information(this, tr("Information"),
|
||||
tr("After pressing OK, press a button on the controller whose "
|
||||
"motion you want to track."),
|
||||
@ -210,7 +213,7 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||
ui->motion_controller_button->setText(tr("[press button]"));
|
||||
ui->motion_controller_button->setFocus();
|
||||
|
||||
input_setter = [=](const Common::ParamPackage& params) {
|
||||
input_setter = [this](const Common::ParamPackage& params) {
|
||||
guid = params.Get("guid", "0");
|
||||
port = params.Get("port", 0);
|
||||
};
|
||||
@ -232,8 +235,9 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||
connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
|
||||
&ConfigureMotionTouch::OnConfigureTouchFromButton);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
|
||||
if (CanCloseDialog())
|
||||
if (CanCloseDialog()) {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -272,15 +276,15 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() {
|
||||
void ConfigureMotionTouch::OnConfigureTouchCalibration() {
|
||||
ui->touch_calibration_config->setEnabled(false);
|
||||
ui->touch_calibration_config->setText(tr("Configuring"));
|
||||
CalibrationConfigurationDialog* dialog = new CalibrationConfigurationDialog(
|
||||
CalibrationConfigurationDialog dialog(
|
||||
this, ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toUInt()),
|
||||
static_cast<u8>(ui->udp_pad_index->currentIndex()), 24872);
|
||||
dialog->exec();
|
||||
if (dialog->completed) {
|
||||
min_x = dialog->min_x;
|
||||
min_y = dialog->min_y;
|
||||
max_x = dialog->max_x;
|
||||
max_y = dialog->max_y;
|
||||
dialog.exec();
|
||||
if (dialog.completed) {
|
||||
min_x = dialog.min_x;
|
||||
min_y = dialog.min_y;
|
||||
max_x = dialog.max_x;
|
||||
max_y = dialog.max_y;
|
||||
LOG_INFO(Frontend,
|
||||
"UDP touchpad calibration config success: min_x={}, min_y={}, max_x={}, max_y={}",
|
||||
min_x, min_y, max_x, max_y);
|
||||
@ -293,10 +297,11 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() {
|
||||
}
|
||||
|
||||
void ConfigureMotionTouch::closeEvent(QCloseEvent* event) {
|
||||
if (CanCloseDialog())
|
||||
if (CanCloseDialog()) {
|
||||
event->accept();
|
||||
else
|
||||
} else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureMotionTouch::ShowUDPTestResult(bool result) {
|
||||
@ -342,16 +347,15 @@ bool ConfigureMotionTouch::CanCloseDialog() {
|
||||
}
|
||||
|
||||
void ConfigureMotionTouch::ApplyConfiguration() {
|
||||
if (!CanCloseDialog())
|
||||
if (!CanCloseDialog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string motion_engine = ui->motion_provider->currentData().toString().toStdString();
|
||||
std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
|
||||
|
||||
Common::ParamPackage motion_param{}, touch_param{};
|
||||
Common::ParamPackage motion_param{};
|
||||
motion_param.Set("engine", motion_engine);
|
||||
touch_param.Set("engine", touch_engine);
|
||||
|
||||
if (motion_engine == "motion_emu") {
|
||||
motion_param.Set("sensitivity", static_cast<float>(ui->motion_sensitivity->value()));
|
||||
} else if (motion_engine == "sdl") {
|
||||
@ -359,6 +363,8 @@ void ConfigureMotionTouch::ApplyConfiguration() {
|
||||
motion_param.Set("port", port);
|
||||
}
|
||||
|
||||
Common::ParamPackage touch_param{};
|
||||
touch_param.Set("engine", touch_engine);
|
||||
if (touch_engine == "cemuhookudp") {
|
||||
touch_param.Set("min_x", min_x);
|
||||
touch_param.Set("min_y", min_y);
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "common/param_package.h"
|
||||
#include "core/settings.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
|
||||
@ -26,11 +26,11 @@ class CalibrationConfigurationDialog : public QDialog {
|
||||
public:
|
||||
explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port,
|
||||
u8 pad_index, u16 client_id);
|
||||
~CalibrationConfigurationDialog();
|
||||
~CalibrationConfigurationDialog() override;
|
||||
|
||||
private:
|
||||
Q_INVOKABLE void UpdateLabelText(QString text);
|
||||
Q_INVOKABLE void UpdateButtonText(QString text);
|
||||
Q_INVOKABLE void UpdateLabelText(const QString& text);
|
||||
Q_INVOKABLE void UpdateButtonText(const QString& text);
|
||||
|
||||
QVBoxLayout* layout;
|
||||
QLabel* status_label;
|
||||
@ -39,7 +39,10 @@ private:
|
||||
|
||||
// Configuration results
|
||||
bool completed{};
|
||||
u16 min_x, min_y, max_x, max_y;
|
||||
u16 min_x{};
|
||||
u16 min_y{};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
|
||||
friend class ConfigureMotionTouch;
|
||||
};
|
||||
@ -81,7 +84,10 @@ private:
|
||||
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
|
||||
|
||||
// Coordinate system of the CemuhookUDP touch provider
|
||||
int min_x, min_y, max_x, max_y;
|
||||
int min_x{};
|
||||
int min_y{};
|
||||
int max_x{};
|
||||
int max_y{};
|
||||
|
||||
bool udp_test_in_progress{};
|
||||
|
||||
|
149
src/citra_qt/configuration/configure_per_game.cpp
Normal file
149
src/citra_qt/configuration/configure_per_game.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/configuration/config.h"
|
||||
#include "citra_qt/configuration/configure_audio.h"
|
||||
#include "citra_qt/configuration/configure_general.h"
|
||||
#include "citra_qt/configuration/configure_graphics.h"
|
||||
#include "citra_qt/configuration/configure_per_game.h"
|
||||
#include "citra_qt/configuration/configure_system.h"
|
||||
#include "citra_qt/util/util.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/loader/smdh.h"
|
||||
#include "ui_configure_per_game.h"
|
||||
|
||||
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
||||
Core::System& system_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
|
||||
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
|
||||
const auto config_file_name = title_id == 0 ? filename : fmt::format("{:016X}", title_id);
|
||||
game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
|
||||
|
||||
audio_tab = std::make_unique<ConfigureAudio>(this);
|
||||
general_tab = std::make_unique<ConfigureGeneral>(this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(this);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->tabWidget->addTab(general_tab.get(), tr("General"));
|
||||
ui->tabWidget->addTab(system_tab.get(), tr("System"));
|
||||
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
|
||||
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
|
||||
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setWindowTitle(tr("Properties"));
|
||||
// remove Help question mark button from the title bar
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
scene = new QGraphicsScene;
|
||||
ui->icon_view->setScene(scene);
|
||||
|
||||
if (system.IsPoweredOn()) {
|
||||
QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply);
|
||||
connect(apply_button, &QAbstractButton::clicked, this,
|
||||
&ConfigurePerGame::HandleApplyButtonClicked);
|
||||
}
|
||||
|
||||
connect(ui->button_reset_per_game, &QPushButton::clicked, this,
|
||||
&ConfigurePerGame::ResetDefaults);
|
||||
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
ConfigurePerGame::~ConfigurePerGame() = default;
|
||||
|
||||
void ConfigurePerGame::ResetDefaults() {
|
||||
const auto config_file_name = title_id == 0 ? filename : fmt::format("{:016X}", title_id);
|
||||
QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
this, tr("Citra"), tr("Are you sure you want to <b>reset your settings for this game</b>?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (answer == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::Delete(fmt::format("{}/custom/{}.ini",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir),
|
||||
config_file_name));
|
||||
close();
|
||||
}
|
||||
|
||||
void ConfigurePerGame::ApplyConfiguration() {
|
||||
general_tab->ApplyConfiguration();
|
||||
system_tab->ApplyConfiguration();
|
||||
graphics_tab->ApplyConfiguration();
|
||||
audio_tab->ApplyConfiguration();
|
||||
|
||||
Settings::LogSettings();
|
||||
|
||||
game_config->Save();
|
||||
}
|
||||
|
||||
void ConfigurePerGame::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
||||
void ConfigurePerGame::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigurePerGame::HandleApplyButtonClicked() {
|
||||
ApplyConfiguration();
|
||||
}
|
||||
|
||||
static QPixmap GetQPixmapFromSMDH(std::vector<u8>& smdh_data) {
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
bool large = true;
|
||||
std::vector<u16> icon_data = smdh.GetIcon(large);
|
||||
const uchar* data = reinterpret_cast<const uchar*>(icon_data.data());
|
||||
int size = large ? 48 : 24;
|
||||
QImage icon(data, size, size, QImage::Format::Format_RGB16);
|
||||
return QPixmap::fromImage(icon);
|
||||
}
|
||||
|
||||
void ConfigurePerGame::LoadConfiguration() {
|
||||
if (filename.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ui->display_title_id->setText(
|
||||
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
|
||||
|
||||
const auto loader = Loader::GetLoader(filename);
|
||||
|
||||
std::string title;
|
||||
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
||||
ui->display_name->setText(QString::fromStdString(title));
|
||||
|
||||
std::vector<u8> bytes;
|
||||
if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
|
||||
scene->clear();
|
||||
|
||||
QPixmap map = GetQPixmapFromSMDH(bytes);
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
ui->display_filepath->setText(QString::fromStdString(filename));
|
||||
|
||||
ui->display_format->setText(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
|
||||
|
||||
const auto valueText = ReadableByteSize(FileUtil::GetSize(filename));
|
||||
ui->display_size->setText(valueText);
|
||||
}
|
69
src/citra_qt/configuration/configure_per_game.h
Normal file
69
src/citra_qt/configuration/configure_per_game.h
Normal file
@ -0,0 +1,69 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include "citra_qt/configuration/config.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class ConfigureAudio;
|
||||
class ConfigureGeneral;
|
||||
class ConfigureGraphics;
|
||||
class ConfigureSystem;
|
||||
|
||||
class QGraphicsScene;
|
||||
class QStandardItem;
|
||||
class QStandardItemModel;
|
||||
class QTreeView;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigurePerGame;
|
||||
}
|
||||
|
||||
class ConfigurePerGame : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
|
||||
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
|
||||
Core::System& system_);
|
||||
~ConfigurePerGame() override;
|
||||
|
||||
/// Loads all button configurations to settings file
|
||||
void LoadConfiguration();
|
||||
|
||||
/// Save all button configurations to settings file
|
||||
void ApplyConfiguration();
|
||||
|
||||
/// Reset the settings for this game
|
||||
void ResetDefaults();
|
||||
|
||||
private:
|
||||
void changeEvent(QEvent* event) override;
|
||||
void RetranslateUI();
|
||||
|
||||
void HandleApplyButtonClicked();
|
||||
|
||||
std::unique_ptr<Ui::ConfigurePerGame> ui;
|
||||
std::string filename;
|
||||
u64 title_id;
|
||||
|
||||
QGraphicsScene* scene;
|
||||
|
||||
std::unique_ptr<Config> game_config;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
std::unique_ptr<ConfigureAudio> audio_tab;
|
||||
std::unique_ptr<ConfigureGeneral> general_tab;
|
||||
std::unique_ptr<ConfigureGraphics> graphics_tab;
|
||||
std::unique_ptr<ConfigureSystem> system_tab;
|
||||
};
|
271
src/citra_qt/configuration/configure_per_game.ui
Normal file
271
src/citra_qt/configuration/configure_per_game.ui
Normal file
@ -0,0 +1,271 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigurePerGame</class>
|
||||
<widget class="QDialog" name="ConfigurePerGame">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>900</width>
|
||||
<height>661</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>900</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Info</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGraphicsView" name="icon_view">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="interactive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="display_filepath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>160</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="display_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Filepath</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Title ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="display_title_id">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="display_name">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="display_format">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="button_reset_per_game">
|
||||
<property name="text">
|
||||
<string>Reset Game Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigurePerGame</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfigurePerGame</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -6,8 +6,8 @@
|
||||
#include <QFileDialog>
|
||||
#include <QUrl>
|
||||
#include "citra_qt/configuration/configure_storage.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_storage.h"
|
||||
|
||||
ConfigureStorage::ConfigureStorage(QWidget* parent)
|
||||
@ -60,7 +60,7 @@ ConfigureStorage::ConfigureStorage(QWidget* parent)
|
||||
ConfigureStorage::~ConfigureStorage() = default;
|
||||
|
||||
void ConfigureStorage::SetConfiguration() {
|
||||
ui->nand_group->setVisible(Settings::values.use_custom_storage);
|
||||
ui->nand_group->setVisible(Settings::values.use_custom_storage.GetValue());
|
||||
QString nand_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
ui->nand_dir_path->setText(nand_path);
|
||||
ui->open_nand_dir->setEnabled(!nand_path.isEmpty());
|
||||
@ -71,8 +71,8 @@ void ConfigureStorage::SetConfiguration() {
|
||||
ui->sdmc_dir_path->setText(sdmc_path);
|
||||
ui->open_sdmc_dir->setEnabled(!sdmc_path.isEmpty());
|
||||
|
||||
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd);
|
||||
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage);
|
||||
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue());
|
||||
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue());
|
||||
|
||||
ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_system.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_system.h"
|
||||
|
||||
static const std::array<int, 12> days_in_month = {{
|
||||
@ -249,10 +249,14 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
|
||||
// This scales across DPIs. (This value should be enough for "xxx%")
|
||||
ui->clock_display_label->setMinimumWidth(40);
|
||||
|
||||
connect(ui->slider_clock_speed, &QSlider::valueChanged, [&](int value) {
|
||||
connect(ui->slider_clock_speed, &QSlider::valueChanged, this, [&](int value) {
|
||||
ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value)));
|
||||
});
|
||||
|
||||
ui->clock_speed_label->setVisible(Settings::IsConfiguringGlobal());
|
||||
ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||
|
||||
SetupPerGameUI();
|
||||
ConfigureTime();
|
||||
}
|
||||
|
||||
@ -261,11 +265,19 @@ ConfigureSystem::~ConfigureSystem() = default;
|
||||
void ConfigureSystem::SetConfiguration() {
|
||||
enabled = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock));
|
||||
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue()));
|
||||
QDateTime date_time;
|
||||
date_time.setTime_t(Settings::values.init_time);
|
||||
date_time.setTime_t(Settings::values.init_time.GetValue());
|
||||
ui->edit_init_time->setDateTime(date_time);
|
||||
|
||||
long long init_time_offset = Settings::values.init_time_offset.GetValue();
|
||||
long long days_offset = init_time_offset / 86400;
|
||||
ui->edit_init_time_offset_days->setValue(days_offset);
|
||||
|
||||
unsigned long long time_offset = std::abs(init_time_offset) - std::abs(days_offset * 86400);
|
||||
QTime time = QTime::fromMSecsSinceStartOfDay(time_offset * 1000);
|
||||
ui->edit_init_time_offset_time->setTime(time);
|
||||
|
||||
if (!enabled) {
|
||||
cfg = Service::CFG::GetModule(Core::System::GetInstance());
|
||||
ASSERT_MSG(cfg, "CFG Module missing!");
|
||||
@ -279,20 +291,31 @@ void ConfigureSystem::SetConfiguration() {
|
||||
ui->label_disable_info->hide();
|
||||
}
|
||||
|
||||
ui->slider_clock_speed->setValue(SettingsToSlider(Settings::values.cpu_clock_percentage));
|
||||
ui->clock_display_label->setText(
|
||||
QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage));
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.cpu_clock_percentage.UsingGlobal()) {
|
||||
ui->clock_speed_combo->setCurrentIndex(0);
|
||||
ui->slider_clock_speed->setEnabled(false);
|
||||
} else {
|
||||
ui->clock_speed_combo->setCurrentIndex(1);
|
||||
ui->slider_clock_speed->setEnabled(true);
|
||||
}
|
||||
ConfigurationShared::SetHighlight(ui->clock_speed_widget,
|
||||
!Settings::values.cpu_clock_percentage.UsingGlobal());
|
||||
}
|
||||
|
||||
ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds);
|
||||
ui->slider_clock_speed->setValue(
|
||||
SettingsToSlider(Settings::values.cpu_clock_percentage.GetValue()));
|
||||
ui->clock_display_label->setText(
|
||||
QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage.GetValue()));
|
||||
ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds.GetValue());
|
||||
ui->plugin_loader->setChecked(Settings::values.plugin_loader_enabled.GetValue());
|
||||
ui->allow_plugin_loader->setChecked(Settings::values.allow_plugin_loader.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureSystem::ReadSystemSettings() {
|
||||
// set username
|
||||
username = cfg->GetUsername();
|
||||
// TODO(wwylele): Use this when we move to Qt 5.5
|
||||
// ui->edit_username->setText(QString::fromStdU16String(username));
|
||||
ui->edit_username->setText(
|
||||
QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
|
||||
ui->edit_username->setText(QString::fromStdU16String(username));
|
||||
|
||||
// set birthday
|
||||
std::tie(birthmonth, birthday) = cfg->GetBirthday();
|
||||
@ -329,32 +352,29 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
bool modified = false;
|
||||
|
||||
// apply username
|
||||
// TODO(wwylele): Use this when we move to Qt 5.5
|
||||
// std::u16string new_username = ui->edit_username->text().toStdU16String();
|
||||
std::u16string new_username(
|
||||
reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
|
||||
std::u16string new_username = ui->edit_username->text().toStdU16String();
|
||||
if (new_username != username) {
|
||||
cfg->SetUsername(new_username);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// apply birthday
|
||||
int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
|
||||
int new_birthday = ui->combo_birthday->currentIndex() + 1;
|
||||
s32 new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
|
||||
s32 new_birthday = ui->combo_birthday->currentIndex() + 1;
|
||||
if (birthmonth != new_birthmonth || birthday != new_birthday) {
|
||||
cfg->SetBirthday(new_birthmonth, new_birthday);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// apply language
|
||||
int new_language = ui->combo_language->currentIndex();
|
||||
s32 new_language = ui->combo_language->currentIndex();
|
||||
if (language_index != new_language) {
|
||||
cfg->SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language));
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// apply sound
|
||||
int new_sound = ui->combo_sound->currentIndex();
|
||||
s32 new_sound = ui->combo_sound->currentIndex();
|
||||
if (sound_index != new_sound) {
|
||||
cfg->SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound));
|
||||
modified = true;
|
||||
@ -378,15 +398,30 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
cfg->UpdateConfigNANDSavegame();
|
||||
}
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.is_new_3ds, ui->toggle_new_3ds,
|
||||
is_new_3ds);
|
||||
|
||||
Settings::values.init_clock =
|
||||
static_cast<Settings::InitClock>(ui->combo_init_clock->currentIndex());
|
||||
Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t();
|
||||
|
||||
Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked();
|
||||
s64 time_offset_time = ui->edit_init_time_offset_time->time().msecsSinceStartOfDay() / 1000;
|
||||
s64 time_offset_days = ui->edit_init_time_offset_days->value() * 86400;
|
||||
|
||||
if (time_offset_days < 0) {
|
||||
time_offset_time = -time_offset_time;
|
||||
}
|
||||
|
||||
Settings::values.cpu_clock_percentage = SliderToSettings(ui->slider_clock_speed->value());
|
||||
Settings::Apply();
|
||||
Settings::values.init_time_offset = time_offset_days + time_offset_time;
|
||||
Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked();
|
||||
|
||||
Settings::values.plugin_loader_enabled.SetValue(ui->plugin_loader->isChecked());
|
||||
Settings::values.allow_plugin_loader.SetValue(ui->allow_plugin_loader->isChecked());
|
||||
}
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.cpu_clock_percentage, ui->clock_speed_combo,
|
||||
[this](s32) { return SliderToSettings(ui->slider_clock_speed->value()); });
|
||||
}
|
||||
|
||||
void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
|
||||
@ -394,10 +429,10 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
|
||||
return;
|
||||
|
||||
// store current day selection
|
||||
int birthday_index = ui->combo_birthday->currentIndex();
|
||||
s32 birthday_index = ui->combo_birthday->currentIndex();
|
||||
|
||||
// get number of days in the new selected month
|
||||
int days = days_in_month[birthmonth_index];
|
||||
s32 days = days_in_month[birthmonth_index];
|
||||
|
||||
// if the selected day is out of range,
|
||||
// reset it to 1st
|
||||
@ -406,7 +441,7 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
|
||||
|
||||
// update the day combo box
|
||||
ui->combo_birthday->clear();
|
||||
for (int i = 1; i <= days; ++i) {
|
||||
for (s32 i = 1; i <= days; ++i) {
|
||||
ui->combo_birthday->addItem(QString::number(i));
|
||||
}
|
||||
|
||||
@ -415,10 +450,10 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
|
||||
}
|
||||
|
||||
void ConfigureSystem::ConfigureTime() {
|
||||
ui->edit_init_time->setCalendarPopup(true);
|
||||
QDateTime dt;
|
||||
dt.fromString(QStringLiteral("2000-01-01 00:00:01"), QStringLiteral("yyyy-MM-dd hh:mm:ss"));
|
||||
ui->edit_init_time->setMinimumDateTime(dt);
|
||||
ui->edit_init_time->setCalendarPopup(true);
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
@ -426,10 +461,16 @@ void ConfigureSystem::ConfigureTime() {
|
||||
}
|
||||
|
||||
void ConfigureSystem::UpdateInitTime(int init_clock) {
|
||||
const bool is_global = Settings::IsConfiguringGlobal();
|
||||
const bool is_fixed_time =
|
||||
static_cast<Settings::InitClock>(init_clock) == Settings::InitClock::FixedTime;
|
||||
ui->label_init_time->setVisible(is_fixed_time);
|
||||
ui->edit_init_time->setVisible(is_fixed_time);
|
||||
|
||||
ui->label_init_time->setVisible(is_fixed_time && is_global);
|
||||
ui->edit_init_time->setVisible(is_fixed_time && is_global);
|
||||
|
||||
ui->label_init_time_offset->setVisible(!is_fixed_time && is_global);
|
||||
ui->edit_init_time_offset_days->setVisible(!is_fixed_time && is_global);
|
||||
ui->edit_init_time_offset_time->setVisible(!is_fixed_time && is_global);
|
||||
}
|
||||
|
||||
void ConfigureSystem::RefreshConsoleID() {
|
||||
@ -454,3 +495,50 @@ void ConfigureSystem::RefreshConsoleID() {
|
||||
void ConfigureSystem::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureSystem::SetupPerGameUI() {
|
||||
// Block the global settings if a game is currently running that overrides them
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal());
|
||||
ui->slider_clock_speed->setEnabled(Settings::values.cpu_clock_percentage.UsingGlobal());
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide most settings for now, we can implement them later
|
||||
ui->label_username->setVisible(false);
|
||||
ui->label_birthday->setVisible(false);
|
||||
ui->label_init_clock->setVisible(false);
|
||||
ui->label_init_time->setVisible(false);
|
||||
ui->label_console_id->setVisible(false);
|
||||
ui->label_sound->setVisible(false);
|
||||
ui->label_language->setVisible(false);
|
||||
ui->label_country->setVisible(false);
|
||||
ui->label_play_coins->setVisible(false);
|
||||
ui->edit_username->setVisible(false);
|
||||
ui->spinBox_play_coins->setVisible(false);
|
||||
ui->combo_birthday->setVisible(false);
|
||||
ui->combo_birthmonth->setVisible(false);
|
||||
ui->combo_init_clock->setVisible(false);
|
||||
ui->combo_sound->setVisible(false);
|
||||
ui->combo_language->setVisible(false);
|
||||
ui->combo_country->setVisible(false);
|
||||
ui->label_init_time_offset->setVisible(false);
|
||||
ui->edit_init_time_offset_days->setVisible(false);
|
||||
ui->edit_init_time_offset_time->setVisible(false);
|
||||
ui->button_regenerate_console_id->setVisible(false);
|
||||
// Apps can change the state of the plugin loader, so plugins load
|
||||
// to a chainloaded app with specific parameters. Don't allow
|
||||
// the plugin loader state to be configured per-game as it may
|
||||
// mess things up.
|
||||
ui->label_plugin_loader->setVisible(false);
|
||||
ui->plugin_loader->setVisible(false);
|
||||
ui->allow_plugin_loader->setVisible(false);
|
||||
|
||||
connect(ui->clock_speed_combo, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->slider_clock_speed->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->clock_speed_widget, index == 1);
|
||||
});
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
|
||||
is_new_3ds);
|
||||
}
|
||||
|
@ -12,6 +12,10 @@ namespace Ui {
|
||||
class ConfigureSystem;
|
||||
}
|
||||
|
||||
namespace ConfigurationShared {
|
||||
enum class CheckState;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
namespace CFG {
|
||||
class Module;
|
||||
@ -37,6 +41,9 @@ private:
|
||||
void UpdateInitTime(int init_clock);
|
||||
void RefreshConsoleID();
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
ConfigurationShared::CheckState is_new_3ds;
|
||||
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||
bool enabled = false;
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>564</height>
|
||||
<width>525</width>
|
||||
<height>619</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -258,6 +258,44 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QDateTimeEdit" name="edit_init_time">
|
||||
<property name="displayFormat">
|
||||
<string>yyyy-MM-ddTHH:mm:ss</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_init_time_offset">
|
||||
<property name="text">
|
||||
<string>Offset time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<layout class="QGridLayout" name="edit_init_time_offset_grid">
|
||||
<item column="0">
|
||||
<widget class="QSpinBox" name="edit_init_time_offset_days">
|
||||
<property name="suffix">
|
||||
<string> days</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-2147483648</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>2147483647</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item column="1">
|
||||
<widget class="QTimeEdit" name="edit_init_time_offset_time">
|
||||
<property name="displayFormat">
|
||||
<string>HH:mm:ss</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QSpinBox" name="spinBox_play_coins">
|
||||
<property name="maximum">
|
||||
@ -295,13 +333,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QDateTimeEdit" name="edit_init_time">
|
||||
<property name="displayFormat">
|
||||
<string>yyyy-MM-ddTHH:mm:ss</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="toggle_new_3ds">
|
||||
<property name="text">
|
||||
@ -309,23 +340,64 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="label_plugin_loader">
|
||||
<property name="text">
|
||||
<string>3GX Plugin Loader:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="plugin_loader">
|
||||
<property name="text">
|
||||
<string>Enable 3GX plugin loader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QCheckBox" name="allow_plugin_loader">
|
||||
<property name="text">
|
||||
<string>Allow games to change plugin loader state</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<widget class="QGroupBox" name="group_advanced">
|
||||
<property name="title">
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<widget class="QWidget" name="clock_speed_widget" native="true">
|
||||
<layout class="QHBoxLayout" name="clock_speed_layout">
|
||||
<property name="spacing">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="clock_speed_combo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global clock speed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set clock speed:</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="clock_speed_label">
|
||||
<property name="text">
|
||||
<string>CPU Clock Speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<item>
|
||||
<widget class="QSlider" name="slider_clock_speed">
|
||||
<property name="toolTip">
|
||||
<string><html><body>Changes the emulated CPU clock frequency.<br>Underclocking can increase performance but may cause the game to freeze.<br>Overclocking may reduce in game lag but also might cause freezes</body></html></string>
|
||||
@ -353,7 +425,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item>
|
||||
<widget class="QLabel" name="clock_display_label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
@ -366,6 +438,9 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
|
||||
</item>
|
||||
@ -418,7 +493,6 @@
|
||||
<tabstop>edit_init_time</tabstop>
|
||||
<tabstop>spinBox_play_coins</tabstop>
|
||||
<tabstop>button_regenerate_console_id</tabstop>
|
||||
<tabstop>slider_clock_speed</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -73,11 +73,11 @@ ConfigureTouchFromButton::ConfigureTouchFromButton(
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()), touch_maps(touch_maps),
|
||||
selected_index(default_index), timeout_timer(std::make_unique<QTimer>()),
|
||||
poll_timer(std::make_unique<QTimer>()) {
|
||||
|
||||
ui->setupUi(this);
|
||||
binding_list_model = std::make_unique<QStandardItemModel>(0, 3, this);
|
||||
binding_list_model->setHorizontalHeaderLabels({tr("Button"), tr("X"), tr("Y")});
|
||||
ui->binding_list->setModel(binding_list_model.get());
|
||||
binding_list_model = new QStandardItemModel(0, 3, this);
|
||||
binding_list_model->setHorizontalHeaderLabels(
|
||||
{tr("Button"), tr("X", "X axis"), tr("Y", "Y axis")});
|
||||
ui->binding_list->setModel(binding_list_model);
|
||||
ui->bottom_screen->SetCoordLabel(ui->coord_label);
|
||||
|
||||
SetConfiguration();
|
||||
@ -93,11 +93,12 @@ void ConfigureTouchFromButton::showEvent(QShowEvent* ev) {
|
||||
// width values are not valid in the constructor
|
||||
const int w =
|
||||
ui->binding_list->viewport()->contentsRect().width() / binding_list_model->columnCount();
|
||||
if (w > 0) {
|
||||
if (w <= 0) {
|
||||
return;
|
||||
}
|
||||
ui->binding_list->setColumnWidth(0, w);
|
||||
ui->binding_list->setColumnWidth(1, w);
|
||||
ui->binding_list->setColumnWidth(2, w);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureTouchFromButton::SetConfiguration() {
|
||||
@ -123,7 +124,7 @@ void ConfigureTouchFromButton::UpdateUiDisplay() {
|
||||
QStandardItem* ycoord = new QStandardItem(QString::number(package.Get("y", 0)));
|
||||
binding_list_model->appendRow({button, xcoord, ycoord});
|
||||
|
||||
int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0));
|
||||
const int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0));
|
||||
button->setData(dot, DataRoleDot);
|
||||
}
|
||||
}
|
||||
@ -145,7 +146,7 @@ void ConfigureTouchFromButton::ConnectEvents() {
|
||||
&ConfigureTouchFromButton::EditBinding);
|
||||
connect(ui->binding_list->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
&ConfigureTouchFromButton::OnBindingSelection);
|
||||
connect(binding_list_model.get(), &QStandardItemModel::itemChanged, this,
|
||||
connect(binding_list_model, &QStandardItemModel::itemChanged, this,
|
||||
&ConfigureTouchFromButton::OnBindingChanged);
|
||||
connect(ui->binding_list->model(), &QStandardItemModel::rowsAboutToBeRemoved, this,
|
||||
&ConfigureTouchFromButton::OnBindingDeleted);
|
||||
@ -232,7 +233,7 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is
|
||||
|
||||
input_setter = [this, row_index, is_new](const Common::ParamPackage& params,
|
||||
const bool cancel) {
|
||||
auto cell = binding_list_model->item(row_index, 0);
|
||||
auto* cell = binding_list_model->item(row_index, 0);
|
||||
if (cancel) {
|
||||
if (is_new) {
|
||||
binding_list_model->removeRow(row_index);
|
||||
@ -260,15 +261,15 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is
|
||||
}
|
||||
|
||||
void ConfigureTouchFromButton::NewBinding(const QPoint& pos) {
|
||||
QStandardItem* button = new QStandardItem();
|
||||
auto* button = new QStandardItem();
|
||||
button->setEditable(false);
|
||||
QStandardItem* xcoord = new QStandardItem(QString::number(pos.x()));
|
||||
QStandardItem* ycoord = new QStandardItem(QString::number(pos.y()));
|
||||
auto* x_coord = new QStandardItem(QString::number(pos.x()));
|
||||
auto* y_coord = new QStandardItem(QString::number(pos.y()));
|
||||
|
||||
const int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y());
|
||||
button->setData(dot_id, DataRoleDot);
|
||||
|
||||
binding_list_model->appendRow({button, xcoord, ycoord});
|
||||
binding_list_model->appendRow({button, x_coord, y_coord});
|
||||
ui->binding_list->setFocus();
|
||||
ui->binding_list->setCurrentIndex(button->index());
|
||||
|
||||
@ -283,11 +284,11 @@ void ConfigureTouchFromButton::EditBinding(const QModelIndex& qi) {
|
||||
|
||||
void ConfigureTouchFromButton::DeleteBinding() {
|
||||
const int row_index = ui->binding_list->currentIndex().row();
|
||||
if (row_index >= 0) {
|
||||
ui->bottom_screen->RemoveDot(
|
||||
binding_list_model->index(row_index, 0).data(DataRoleDot).toInt());
|
||||
binding_list_model->removeRow(row_index);
|
||||
if (row_index < 0) {
|
||||
return;
|
||||
}
|
||||
ui->bottom_screen->RemoveDot(binding_list_model->index(row_index, 0).data(DataRoleDot).toInt());
|
||||
binding_list_model->removeRow(row_index);
|
||||
}
|
||||
|
||||
void ConfigureTouchFromButton::OnBindingSelection(const QItemSelection& selected,
|
||||
@ -329,7 +330,7 @@ void ConfigureTouchFromButton::OnBindingChanged(QStandardItem* item) {
|
||||
void ConfigureTouchFromButton::OnBindingDeleted([[maybe_unused]] const QModelIndex& parent,
|
||||
int first, int last) {
|
||||
for (int i = first; i <= last; ++i) {
|
||||
auto ix = binding_list_model->index(i, 0);
|
||||
const auto ix = binding_list_model->index(i, 0);
|
||||
if (!ix.isValid()) {
|
||||
return;
|
||||
}
|
||||
@ -422,7 +423,7 @@ int TouchScreenPreview::AddDot(const int device_x, const int device_y) {
|
||||
dot_font.setStyleHint(QFont::Monospace);
|
||||
dot_font.setPointSize(20);
|
||||
|
||||
QLabel* dot = new QLabel(this);
|
||||
auto* dot = new QLabel(this);
|
||||
dot->setAttribute(Qt::WA_TranslucentBackground);
|
||||
dot->setFont(dot_font);
|
||||
dot->setText(QChar(0xD7)); // U+00D7 Multiplication Sign
|
||||
@ -440,13 +441,14 @@ int TouchScreenPreview::AddDot(const int device_x, const int device_y) {
|
||||
}
|
||||
|
||||
void TouchScreenPreview::RemoveDot(const int id) {
|
||||
for (auto dot_it = dots.begin(); dot_it != dots.end(); ++dot_it) {
|
||||
if (dot_it->first == id) {
|
||||
dot_it->second->deleteLater();
|
||||
dots.erase(dot_it);
|
||||
const auto iter = std::find_if(dots.begin(), dots.end(),
|
||||
[id](const auto& entry) { return entry.first == id; });
|
||||
if (iter == dots.cend()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
iter->second->deleteLater();
|
||||
dots.erase(iter);
|
||||
}
|
||||
|
||||
void TouchScreenPreview::HighlightDot(const int id, const bool active) const {
|
||||
@ -470,14 +472,15 @@ void TouchScreenPreview::HighlightDot(const int id, const bool active) const {
|
||||
}
|
||||
|
||||
void TouchScreenPreview::MoveDot(const int id, const int device_x, const int device_y) const {
|
||||
for (const auto& dot : dots) {
|
||||
if (dot.first == id) {
|
||||
dot.second->setProperty(PropX, device_x);
|
||||
dot.second->setProperty(PropY, device_y);
|
||||
PositionDot(dot.second, device_x, device_y);
|
||||
const auto iter = std::find_if(dots.begin(), dots.end(),
|
||||
[id](const auto& entry) { return entry.first == id; });
|
||||
if (iter == dots.cend()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
iter->second->setProperty(PropX, device_x);
|
||||
iter->second->setProperty(PropY, device_y);
|
||||
PositionDot(iter->second, device_x, device_y);
|
||||
}
|
||||
|
||||
void TouchScreenPreview::resizeEvent(QResizeEvent* event) {
|
||||
@ -508,7 +511,7 @@ void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) {
|
||||
}
|
||||
const auto pos = MapToDeviceCoords(event->x(), event->y());
|
||||
if (pos) {
|
||||
coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y()));
|
||||
coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x(), pos->y()));
|
||||
} else {
|
||||
coord_label->clear();
|
||||
}
|
||||
@ -521,12 +524,13 @@ void TouchScreenPreview::leaveEvent([[maybe_unused]] QEvent* event) {
|
||||
}
|
||||
|
||||
void TouchScreenPreview::mousePressEvent(QMouseEvent* event) {
|
||||
if (event->button() == Qt::MouseButton::LeftButton) {
|
||||
if (event->button() != Qt::MouseButton::LeftButton) {
|
||||
return;
|
||||
}
|
||||
const auto pos = MapToDeviceCoords(event->x(), event->y());
|
||||
if (pos) {
|
||||
emit DotAdded(*pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) {
|
||||
@ -568,7 +572,7 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) {
|
||||
emit DotMoved(drag_state.dot->property(PropId).toInt(), *device_coord);
|
||||
if (coord_label) {
|
||||
coord_label->setText(
|
||||
QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y()));
|
||||
QStringLiteral("X: %1, Y: %2").arg(device_coord->x(), device_coord->y()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -600,12 +604,17 @@ std::optional<QPoint> TouchScreenPreview::MapToDeviceCoords(const int screen_x,
|
||||
|
||||
void TouchScreenPreview::PositionDot(QLabel* const dot, const int device_x,
|
||||
const int device_y) const {
|
||||
dot->move(static_cast<int>(
|
||||
static_cast<float>(device_x >= 0 ? device_x : dot->property(PropX).toInt()) *
|
||||
(contentsRect().width() - 1) / (Core::kScreenBottomWidth - 1) +
|
||||
contentsMargins().left() - static_cast<float>(dot->width()) / 2 + 0.5f),
|
||||
static_cast<int>(
|
||||
static_cast<float>(device_y >= 0 ? device_y : dot->property(PropY).toInt()) *
|
||||
(contentsRect().height() - 1) / (Core::kScreenBottomHeight - 1) +
|
||||
contentsMargins().top() - static_cast<float>(dot->height()) / 2 + 0.5f));
|
||||
const float device_coord_x =
|
||||
static_cast<float>(device_x >= 0 ? device_x : dot->property(PropX).toInt());
|
||||
const int x_coord = static_cast<int>(
|
||||
device_coord_x * (contentsRect().width() - 1) / (Core::kScreenBottomWidth - 1) +
|
||||
contentsMargins().left() - static_cast<float>(dot->width()) / 2 + 0.5f);
|
||||
|
||||
const float device_coord_y =
|
||||
static_cast<float>(device_y >= 0 ? device_y : dot->property(PropY).toInt());
|
||||
const int y_coord = static_cast<int>(
|
||||
device_coord_y * (contentsRect().height() - 1) / (Core::kScreenBottomHeight - 1) +
|
||||
contentsMargins().top() - static_cast<float>(dot->height()) / 2 + 0.5f);
|
||||
|
||||
dot->move(x_coord, y_coord);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user