early-access version 3976
This commit is contained in:
parent
5304174e08
commit
51de3f7388
87 changed files with 6633 additions and 3698 deletions
|
@ -285,7 +285,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||||
find_package(Boost 1.79.0 REQUIRED context)
|
find_package(Boost 1.79.0 REQUIRED context)
|
||||||
find_package(enet 1.3 MODULE)
|
find_package(enet 1.3 MODULE)
|
||||||
find_package(fmt 9 REQUIRED)
|
find_package(fmt 9 REQUIRED)
|
||||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
|
||||||
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
||||||
find_package(lz4 REQUIRED)
|
find_package(lz4 REQUIRED)
|
||||||
find_package(nlohmann_json 3.8 REQUIRED)
|
find_package(nlohmann_json 3.8 REQUIRED)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3975.
|
This is the source code for early-access 3976.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
8
externals/CMakeLists.txt
vendored
8
externals/CMakeLists.txt
vendored
|
@ -34,11 +34,6 @@ endif()
|
||||||
# Glad
|
# Glad
|
||||||
add_subdirectory(glad)
|
add_subdirectory(glad)
|
||||||
|
|
||||||
# inih
|
|
||||||
if (NOT TARGET inih::INIReader)
|
|
||||||
add_subdirectory(inih)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# mbedtls
|
# mbedtls
|
||||||
add_subdirectory(mbedtls)
|
add_subdirectory(mbedtls)
|
||||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||||
|
@ -295,3 +290,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||||
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# SimpleIni
|
||||||
|
add_subdirectory(simpleini)
|
||||||
|
|
|
@ -187,6 +187,7 @@ add_subdirectory(audio_core)
|
||||||
add_subdirectory(video_core)
|
add_subdirectory(video_core)
|
||||||
add_subdirectory(network)
|
add_subdirectory(network)
|
||||||
add_subdirectory(input_common)
|
add_subdirectory(input_common)
|
||||||
|
add_subdirectory(frontend_common)
|
||||||
add_subdirectory(shader_recompiler)
|
add_subdirectory(shader_recompiler)
|
||||||
|
|
||||||
if (YUZU_ROOM)
|
if (YUZU_ROOM)
|
||||||
|
|
|
@ -219,7 +219,6 @@ dependencies {
|
||||||
implementation("io.coil-kt:coil:2.2.2")
|
implementation("io.coil-kt:coil:2.2.2")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.window:window:1.2.0-beta03")
|
implementation("androidx.window:window:1.2.0-beta03")
|
||||||
implementation("org.ini4j:ini4j:0.5.4")
|
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||||
|
|
|
@ -230,8 +230,6 @@ object NativeLibrary {
|
||||||
*/
|
*/
|
||||||
external fun onTouchReleased(finger_id: Int)
|
external fun onTouchReleased(finger_id: Int)
|
||||||
|
|
||||||
external fun reloadSettings()
|
|
||||||
|
|
||||||
external fun initGameIni(gameID: String?)
|
external fun initGameIni(gameID: String?)
|
||||||
|
|
||||||
external fun setAppDirectory(directory: String)
|
external fun setAppDirectory(directory: String)
|
||||||
|
|
|
@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
|
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
|
||||||
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
|
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
val isEmulationActive = emulationViewModel.emulationStarted.value &&
|
||||||
|
!emulationViewModel.isEmulationStopping.value
|
||||||
pictureInPictureParamsBuilder.setAutoEnterEnabled(
|
pictureInPictureParamsBuilder.setAutoEnterEnabled(
|
||||||
BooleanSetting.PICTURE_IN_PICTURE.boolean
|
BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
|
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.text.TextUtils
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
|
|
||||||
object Settings {
|
object Settings {
|
||||||
private val context get() = YuzuApplication.appContext
|
private val context get() = YuzuApplication.appContext
|
||||||
|
@ -19,7 +19,7 @@ object Settings {
|
||||||
context.getString(R.string.ini_saved),
|
context.getString(R.string.ini_saved),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
|
NativeConfig.saveSettings()
|
||||||
} else {
|
} else {
|
||||||
// TODO: Save custom game settings
|
// TODO: Save custom game settings
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|
|
@ -21,7 +21,6 @@ import androidx.navigation.navArgs
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
||||||
|
@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
settingsViewModel.shouldSave = false
|
settingsViewModel.shouldSave = false
|
||||||
|
|
||||||
// Delete settings file because the user may have changed values that do not exist in the UI
|
// Delete settings file because the user may have changed values that do not exist in the UI
|
||||||
|
NativeConfig.unloadConfig()
|
||||||
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
|
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
|
||||||
if (!settingsFile.delete()) {
|
if (!settingsFile.delete()) {
|
||||||
throw IOException("Failed to delete $settingsFile")
|
throw IOException("Failed to delete $settingsFile")
|
||||||
}
|
}
|
||||||
NativeLibrary.reloadSettings()
|
NativeConfig.initializeConfig()
|
||||||
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
|
|
|
@ -3,15 +3,8 @@
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.utils
|
package org.yuzu.yuzu_emu.features.settings.utils
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import org.ini4j.Wini
|
|
||||||
import org.yuzu.yuzu_emu.R
|
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.*
|
|
||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains static methods for interacting with .ini files in which settings are stored.
|
* Contains static methods for interacting with .ini files in which settings are stored.
|
||||||
|
@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
object SettingsFile {
|
object SettingsFile {
|
||||||
const val FILE_NAME_CONFIG = "config"
|
const val FILE_NAME_CONFIG = "config"
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
|
|
||||||
* telling why it failed.
|
|
||||||
*
|
|
||||||
* @param fileName The target filename without a path or extension.
|
|
||||||
*/
|
|
||||||
fun saveFile(fileName: String) {
|
|
||||||
val ini = getSettingsFile(fileName)
|
|
||||||
try {
|
|
||||||
val wini = Wini(ini)
|
|
||||||
for (specificCategory in Settings.Category.values()) {
|
|
||||||
val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
|
|
||||||
for (setting in Settings.settingsList) {
|
|
||||||
if (setting.key!!.isEmpty()) continue
|
|
||||||
|
|
||||||
val settingCategoryHeader =
|
|
||||||
NativeConfig.getConfigHeader(setting.category.ordinal)
|
|
||||||
val iniSetting: String? = wini.get(categoryHeader, setting.key)
|
|
||||||
if (iniSetting != null || settingCategoryHeader == categoryHeader) {
|
|
||||||
wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wini.store()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
|
|
||||||
val context = YuzuApplication.appContext
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.error_saving, fileName, e.message),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSettingsFile(fileName: String): File =
|
fun getSettingsFile(fileName: String): File =
|
||||||
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
|
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
|
||||||
}
|
}
|
||||||
|
|
|
@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear existing user data
|
// Clear existing user data
|
||||||
|
NativeConfig.unloadConfig()
|
||||||
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
|
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
|
||||||
|
|
||||||
// Copy archive to internal storage
|
// Copy archive to internal storage
|
||||||
|
@ -650,6 +651,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
|
|
||||||
// Reinitialize relevant data
|
// Reinitialize relevant data
|
||||||
NativeLibrary.initializeSystem(true)
|
NativeLibrary.initializeSystem(true)
|
||||||
|
NativeConfig.initializeConfig()
|
||||||
gamesViewModel.reloadGames(false)
|
gamesViewModel.reloadGames(false)
|
||||||
|
|
||||||
return@newInstance getString(R.string.user_data_import_success)
|
return@newInstance getString(R.string.user_data_import_success)
|
||||||
|
|
|
@ -16,6 +16,7 @@ object DirectoryInitialization {
|
||||||
if (!areDirectoriesReady) {
|
if (!areDirectoriesReady) {
|
||||||
initializeInternalStorage()
|
initializeInternalStorage()
|
||||||
NativeLibrary.initializeSystem(false)
|
NativeLibrary.initializeSystem(false)
|
||||||
|
NativeConfig.initializeConfig()
|
||||||
areDirectoriesReady = true
|
areDirectoriesReady = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,30 @@
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
object NativeConfig {
|
object NativeConfig {
|
||||||
|
/**
|
||||||
|
* Creates a Config object and opens the emulation config.
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
external fun initializeConfig()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the stored config object. This automatically saves the existing config.
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
external fun unloadConfig()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads values saved to the config file and saves them.
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
external fun reloadSettings()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves settings values in memory to disk.
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
external fun saveSettings()
|
||||||
|
|
||||||
external fun getBoolean(key: String, getDefault: Boolean): Boolean
|
external fun getBoolean(key: String, getDefault: Boolean): Boolean
|
||||||
external fun setBoolean(key: String, value: Boolean)
|
external fun setBoolean(key: String, value: Boolean)
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
|
||||||
android_common/android_common.h
|
android_common/android_common.h
|
||||||
applets/software_keyboard.cpp
|
applets/software_keyboard.cpp
|
||||||
applets/software_keyboard.h
|
applets/software_keyboard.h
|
||||||
config.cpp
|
|
||||||
config.h
|
|
||||||
default_ini.h
|
|
||||||
emu_window/emu_window.cpp
|
emu_window/emu_window.cpp
|
||||||
emu_window/emu_window.h
|
emu_window/emu_window.h
|
||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
|
@ -16,15 +13,17 @@ add_library(yuzu-android SHARED
|
||||||
native.cpp
|
native.cpp
|
||||||
native.h
|
native.h
|
||||||
native_config.cpp
|
native_config.cpp
|
||||||
uisettings.cpp
|
android_settings.cpp
|
||||||
game_metadata.cpp
|
game_metadata.cpp
|
||||||
native_log.cpp
|
native_log.cpp
|
||||||
|
android_config.cpp
|
||||||
|
android_config.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
|
||||||
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common)
|
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common)
|
||||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log)
|
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
|
||||||
if (ARCHITECTURE_arm64)
|
if (ARCHITECTURE_arm64)
|
||||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||||
endif()
|
endif()
|
||||||
|
|
70
src/android/app/src/main/jni/android_config.cpp
Executable file
70
src/android/app/src/main/jni/android_config.cpp
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "android_config.h"
|
||||||
|
#include "android_settings.h"
|
||||||
|
#include "common/settings_setting.h"
|
||||||
|
|
||||||
|
AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
|
||||||
|
: Config(config_type) {
|
||||||
|
Initialize(config_name);
|
||||||
|
if (config_type != ConfigType::InputProfile) {
|
||||||
|
ReadAndroidValues();
|
||||||
|
SaveAndroidValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidConfig::~AndroidConfig() {
|
||||||
|
if (global) {
|
||||||
|
AndroidConfig::SaveAllValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::ReloadAllValues() {
|
||||||
|
Reload();
|
||||||
|
ReadAndroidValues();
|
||||||
|
SaveAndroidValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::SaveAllValues() {
|
||||||
|
Save();
|
||||||
|
SaveAndroidValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::ReadAndroidValues() {
|
||||||
|
if (global) {
|
||||||
|
ReadAndroidUIValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::ReadAndroidUIValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Android);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::SaveAndroidValues() {
|
||||||
|
if (global) {
|
||||||
|
SaveAndroidUIValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteToIni();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::SaveAndroidUIValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Android);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
|
||||||
|
auto& map = Settings::values.linkage.by_category;
|
||||||
|
if (map.contains(category)) {
|
||||||
|
return Settings::values.linkage.by_category[category];
|
||||||
|
}
|
||||||
|
return AndroidSettings::values.linkage.by_category[category];
|
||||||
|
}
|
41
src/android/app/src/main/jni/android_config.h
Executable file
41
src/android/app/src/main/jni/android_config.h
Executable file
|
@ -0,0 +1,41 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "frontend_common/config.h"
|
||||||
|
|
||||||
|
class AndroidConfig final : public Config {
|
||||||
|
public:
|
||||||
|
explicit AndroidConfig(const std::string& config_name = "config",
|
||||||
|
ConfigType config_type = ConfigType::GlobalConfig);
|
||||||
|
~AndroidConfig() override;
|
||||||
|
|
||||||
|
void ReloadAllValues() override;
|
||||||
|
void SaveAllValues() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadAndroidValues();
|
||||||
|
void ReadAndroidUIValues();
|
||||||
|
void ReadHidbusValues() override {}
|
||||||
|
void ReadDebugControlValues() override {}
|
||||||
|
void ReadPathValues() override {}
|
||||||
|
void ReadShortcutValues() override {}
|
||||||
|
void ReadUIValues() override {}
|
||||||
|
void ReadUIGamelistValues() override {}
|
||||||
|
void ReadUILayoutValues() override {}
|
||||||
|
void ReadMultiplayerValues() override {}
|
||||||
|
|
||||||
|
void SaveAndroidValues();
|
||||||
|
void SaveAndroidUIValues();
|
||||||
|
void SaveHidbusValues() override {}
|
||||||
|
void SaveDebugControlValues() override {}
|
||||||
|
void SavePathValues() override {}
|
||||||
|
void SaveShortcutValues() override {}
|
||||||
|
void SaveUIValues() override {}
|
||||||
|
void SaveUIGamelistValues() override {}
|
||||||
|
void SaveUILayoutValues() override {}
|
||||||
|
void SaveMultiplayerValues() override {}
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
|
||||||
|
};
|
10
src/android/app/src/main/jni/android_settings.cpp
Executable file
10
src/android/app/src/main/jni/android_settings.cpp
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "android_settings.h"
|
||||||
|
|
||||||
|
namespace AndroidSettings {
|
||||||
|
|
||||||
|
Values values;
|
||||||
|
|
||||||
|
} // namespace AndroidSettings
|
29
src/android/app/src/main/jni/android_settings.h
Executable file
29
src/android/app/src/main/jni/android_settings.h
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/settings_common.h>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/settings_setting.h"
|
||||||
|
|
||||||
|
namespace AndroidSettings {
|
||||||
|
|
||||||
|
struct Values {
|
||||||
|
Settings::Linkage linkage;
|
||||||
|
|
||||||
|
// Android
|
||||||
|
Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture",
|
||||||
|
Settings::Category::Android};
|
||||||
|
Settings::Setting<s32> screen_layout{linkage,
|
||||||
|
5,
|
||||||
|
"screen_layout",
|
||||||
|
Settings::Category::Android,
|
||||||
|
Settings::Specialization::Default,
|
||||||
|
true,
|
||||||
|
true};
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Values values;
|
||||||
|
|
||||||
|
} // namespace AndroidSettings
|
|
@ -52,8 +52,8 @@
|
||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "jni/config.h"
|
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "jni/native.h"
|
#include "jni/native.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
@ -664,8 +664,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
|
||||||
jboolean reload) {
|
jboolean reload) {
|
||||||
// Create the default config.ini.
|
|
||||||
Config{};
|
|
||||||
// Initialize the emulated system.
|
// Initialize the emulated system.
|
||||||
if (!reload) {
|
if (!reload) {
|
||||||
EmulationSession::GetInstance().System().Initialize();
|
EmulationSession::GetInstance().System().Initialize();
|
||||||
|
@ -680,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||||
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
|
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
|
|
||||||
Config{};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_game_id) {
|
|
||||||
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(j_game_id, game_id.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
|
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
|
||||||
jdoubleArray j_stats = env->NewDoubleArray(4);
|
jdoubleArray j_stats = env->NewDoubleArray(4);
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,14 @@
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "android_config.h"
|
||||||
|
#include "android_settings.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "jni/config.h"
|
|
||||||
#include "uisettings.h"
|
std::unique_ptr<AndroidConfig> config;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||||
|
@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
|
||||||
|
config = std::make_unique<AndroidConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
|
||||||
|
config.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
|
||||||
|
config->AndroidConfig::ReloadAllValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
|
||||||
|
config->AndroidConfig::SaveAllValues();
|
||||||
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
|
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
|
||||||
jstring jkey, jboolean getDefault) {
|
jstring jkey, jboolean getDefault) {
|
||||||
auto setting = getSetting<bool>(env, jkey);
|
auto setting = getSetting<bool>(env, jkey);
|
||||||
|
|
|
@ -139,7 +139,7 @@
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/show_fps_text"
|
android:id="@+id/show_fps_text"
|
||||||
style="@style/TextAppearance.Material3.BodyMedium"
|
style="@style/TextAppearance.Material3.BodySmall"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="left"
|
android:layout_gravity="left"
|
||||||
|
@ -147,7 +147,8 @@
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:paddingHorizontal="20dp"
|
android:paddingHorizontal="20dp"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="12sp"
|
android:shadowColor="@android:color/black"
|
||||||
|
android:shadowRadius="3"
|
||||||
tools:ignore="RtlHardcoded" />
|
tools:ignore="RtlHardcoded" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
|
@ -206,9 +206,9 @@ const char* TranslateCategory(Category category) {
|
||||||
case Category::UiAudio:
|
case Category::UiAudio:
|
||||||
return "UiAudio";
|
return "UiAudio";
|
||||||
case Category::UiLayout:
|
case Category::UiLayout:
|
||||||
return "UiLayout";
|
return "UILayout";
|
||||||
case Category::UiGameList:
|
case Category::UiGameList:
|
||||||
return "UiGameList";
|
return "UIGameList";
|
||||||
case Category::Screenshots:
|
case Category::Screenshots:
|
||||||
return "Screenshots";
|
return "Screenshots";
|
||||||
case Category::Shortcuts:
|
case Category::Shortcuts:
|
||||||
|
|
|
@ -232,7 +232,11 @@ struct Values {
|
||||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
||||||
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
|
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
|
||||||
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
|
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
AstcDecodeMode::Cpu,
|
||||||
|
#else
|
||||||
AstcDecodeMode::Gpu,
|
AstcDecodeMode::Gpu,
|
||||||
|
#endif
|
||||||
AstcDecodeMode::Cpu,
|
AstcDecodeMode::Cpu,
|
||||||
AstcDecodeMode::CpuAsynchronous,
|
AstcDecodeMode::CpuAsynchronous,
|
||||||
"accelerate_astc",
|
"accelerate_astc",
|
||||||
|
@ -304,7 +308,11 @@ struct Values {
|
||||||
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
|
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
|
||||||
|
|
||||||
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
|
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
GpuAccuracy::Normal,
|
||||||
|
#else
|
||||||
GpuAccuracy::High,
|
GpuAccuracy::High,
|
||||||
|
#endif
|
||||||
GpuAccuracy::Normal,
|
GpuAccuracy::Normal,
|
||||||
GpuAccuracy::Extreme,
|
GpuAccuracy::Extreme,
|
||||||
"gpu_accuracy",
|
"gpu_accuracy",
|
||||||
|
@ -313,20 +321,38 @@ struct Values {
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
|
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
|
||||||
SwitchableSetting<AnisotropyMode, true> max_anisotropy{
|
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
|
||||||
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
|
#ifdef ANDROID
|
||||||
"max_anisotropy", Category::RendererAdvanced};
|
AnisotropyMode::Default,
|
||||||
|
#else
|
||||||
|
AnisotropyMode::Automatic,
|
||||||
|
#endif
|
||||||
|
AnisotropyMode::Automatic,
|
||||||
|
AnisotropyMode::X16,
|
||||||
|
"max_anisotropy",
|
||||||
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
||||||
AstcRecompression::Uncompressed,
|
AstcRecompression::Uncompressed,
|
||||||
AstcRecompression::Uncompressed,
|
AstcRecompression::Uncompressed,
|
||||||
AstcRecompression::Bc3,
|
AstcRecompression::Bc3,
|
||||||
"astc_recompression",
|
"astc_recompression",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
|
SwitchableSetting<bool> async_presentation{linkage,
|
||||||
Category::RendererAdvanced};
|
#ifdef ANDROID
|
||||||
|
true,
|
||||||
|
#else
|
||||||
|
false,
|
||||||
|
#endif
|
||||||
|
"async_presentation", Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
|
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
|
SwitchableSetting<bool> use_reactive_flushing{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
false,
|
||||||
|
#else
|
||||||
|
true,
|
||||||
|
#endif
|
||||||
|
"use_reactive_flushing",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
|
@ -392,7 +418,11 @@ struct Values {
|
||||||
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
||||||
|
|
||||||
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
ConsoleMode::Handheld,
|
||||||
|
#else
|
||||||
ConsoleMode::Docked,
|
ConsoleMode::Docked,
|
||||||
|
#endif
|
||||||
"use_docked_mode",
|
"use_docked_mode",
|
||||||
Category::System,
|
Category::System,
|
||||||
Specialization::Radio,
|
Specialization::Radio,
|
||||||
|
|
|
@ -521,11 +521,19 @@ add_library(core STATIC
|
||||||
hle/service/grc/grc.h
|
hle/service/grc/grc.h
|
||||||
hle/service/hid/hid.cpp
|
hle/service/hid/hid.cpp
|
||||||
hle/service/hid/hid.h
|
hle/service/hid/hid.h
|
||||||
|
hle/service/hid/hid_debug_server.cpp
|
||||||
|
hle/service/hid/hid_debug_server.h
|
||||||
|
hle/service/hid/hid_server.cpp
|
||||||
|
hle/service/hid/hid_server.h
|
||||||
|
hle/service/hid/hid_system_server.cpp
|
||||||
|
hle/service/hid/hid_system_server.h
|
||||||
hle/service/hid/hidbus.cpp
|
hle/service/hid/hidbus.cpp
|
||||||
hle/service/hid/hidbus.h
|
hle/service/hid/hidbus.h
|
||||||
hle/service/hid/irs.cpp
|
hle/service/hid/irs.cpp
|
||||||
hle/service/hid/irs.h
|
hle/service/hid/irs.h
|
||||||
hle/service/hid/irs_ring_lifo.h
|
hle/service/hid/irs_ring_lifo.h
|
||||||
|
hle/service/hid/resource_manager.cpp
|
||||||
|
hle/service/hid/resource_manager.h
|
||||||
hle/service/hid/ring_lifo.h
|
hle/service/hid/ring_lifo.h
|
||||||
hle/service/hid/xcd.cpp
|
hle/service/hid/xcd.cpp
|
||||||
hle/service/hid/xcd.h
|
hle/service/hid/xcd.h
|
||||||
|
|
|
@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
|
||||||
using ConsoleMotionValues = ConsoleMotionInfo;
|
using ConsoleMotionValues = ConsoleMotionInfo;
|
||||||
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
||||||
|
|
||||||
struct TouchFinger {
|
|
||||||
u64 last_touch{};
|
|
||||||
Common::Point<float> position{};
|
|
||||||
u32 id{};
|
|
||||||
TouchAttribute attribute{};
|
|
||||||
bool pressed{};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Contains all motion related data that is used on the services
|
// Contains all motion related data that is used on the services
|
||||||
struct ConsoleMotion {
|
struct ConsoleMotion {
|
||||||
Common::Vec3f accel{};
|
Common::Vec3f accel{};
|
||||||
|
|
|
@ -356,6 +356,14 @@ struct TouchState {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
|
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
|
||||||
|
|
||||||
|
struct TouchFinger {
|
||||||
|
u64 last_touch{};
|
||||||
|
Common::Point<float> position{};
|
||||||
|
u32 id{};
|
||||||
|
TouchAttribute attribute{};
|
||||||
|
bool pressed{};
|
||||||
|
};
|
||||||
|
|
||||||
// This is nn::hid::TouchScreenConfigurationForNx
|
// This is nn::hid::TouchScreenConfigurationForNx
|
||||||
struct TouchScreenConfigurationForNx {
|
struct TouchScreenConfigurationForNx {
|
||||||
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
|
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
|
||||||
|
|
|
@ -5,13 +5,14 @@
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
#include "core/hid/input_interpreter.h"
|
#include "core/hid/input_interpreter.h"
|
||||||
#include "core/hle/service/hid/controllers/npad.h"
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
#include "core/hle/service/hid/hid.h"
|
#include "core/hle/service/hid/hid_server.h"
|
||||||
|
#include "core/hle/service/hid/resource_manager.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
|
||||||
InputInterpreter::InputInterpreter(Core::System& system)
|
InputInterpreter::InputInterpreter(Core::System& system)
|
||||||
: npad{system.ServiceManager()
|
: npad{system.ServiceManager()
|
||||||
.GetService<Service::HID::Hid>("hid")
|
.GetService<Service::HID::IHidServer>("hid")
|
||||||
->GetAppletResource()
|
->GetResourceManager()
|
||||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
|
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
|
||||||
ResetButtonStates();
|
ResetButtonStates();
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,220 +3,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
namespace Core {
|
||||||
|
class System;
|
||||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Core::Timing {
|
|
||||||
struct EventType;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Service::SM {
|
|
||||||
class ServiceManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
|
|
||||||
enum class HidController : std::size_t {
|
|
||||||
DebugPad,
|
|
||||||
Touchscreen,
|
|
||||||
Mouse,
|
|
||||||
Keyboard,
|
|
||||||
XPad,
|
|
||||||
HomeButton,
|
|
||||||
SleepButton,
|
|
||||||
CaptureButton,
|
|
||||||
InputDetector,
|
|
||||||
UniquePad,
|
|
||||||
NPad,
|
|
||||||
Gesture,
|
|
||||||
ConsoleSixAxisSensor,
|
|
||||||
DebugMouse,
|
|
||||||
Palma,
|
|
||||||
|
|
||||||
MaxControllers,
|
|
||||||
};
|
|
||||||
|
|
||||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
|
||||||
public:
|
|
||||||
explicit IAppletResource(Core::System& system_,
|
|
||||||
KernelHelpers::ServiceContext& service_context_);
|
|
||||||
~IAppletResource() override;
|
|
||||||
|
|
||||||
void ActivateController(HidController controller);
|
|
||||||
void DeactivateController(HidController controller);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T& GetController(HidController controller) {
|
|
||||||
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const T& GetController(HidController controller) const {
|
|
||||||
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T>
|
|
||||||
void MakeController(HidController controller, u8* shared_memory) {
|
|
||||||
if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
|
|
||||||
controllers[static_cast<std::size_t>(controller)] =
|
|
||||||
std::make_unique<T>(system, shared_memory);
|
|
||||||
} else {
|
|
||||||
controllers[static_cast<std::size_t>(controller)] =
|
|
||||||
std::make_unique<T>(system.HIDCore(), shared_memory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
|
|
||||||
controllers[static_cast<std::size_t>(controller)] =
|
|
||||||
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetSharedMemoryHandle(HLERequestContext& ctx);
|
|
||||||
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
|
||||||
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
|
||||||
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
|
||||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
|
||||||
|
|
||||||
KernelHelpers::ServiceContext& service_context;
|
|
||||||
|
|
||||||
std::shared_ptr<Core::Timing::EventType> npad_update_event;
|
|
||||||
std::shared_ptr<Core::Timing::EventType> default_update_event;
|
|
||||||
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
|
||||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
|
||||||
|
|
||||||
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
|
||||||
controllers{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class Hid final : public ServiceFramework<Hid> {
|
|
||||||
public:
|
|
||||||
explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
|
|
||||||
~Hid() override;
|
|
||||||
|
|
||||||
std::shared_ptr<IAppletResource> GetAppletResource();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void CreateAppletResource(HLERequestContext& ctx);
|
|
||||||
void ActivateDebugPad(HLERequestContext& ctx);
|
|
||||||
void ActivateTouchScreen(HLERequestContext& ctx);
|
|
||||||
void ActivateMouse(HLERequestContext& ctx);
|
|
||||||
void ActivateKeyboard(HLERequestContext& ctx);
|
|
||||||
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
|
|
||||||
void ActivateXpad(HLERequestContext& ctx);
|
|
||||||
void GetXpadIDs(HLERequestContext& ctx);
|
|
||||||
void ActivateSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void DeactivateSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void StartSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void StopSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
|
|
||||||
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
|
|
||||||
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
|
||||||
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
|
||||||
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
|
||||||
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
|
||||||
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
|
||||||
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
|
||||||
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
|
|
||||||
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
|
|
||||||
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
|
|
||||||
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
|
|
||||||
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
|
|
||||||
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
|
|
||||||
void ActivateGesture(HLERequestContext& ctx);
|
|
||||||
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
|
|
||||||
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
|
|
||||||
void SetSupportedNpadIdType(HLERequestContext& ctx);
|
|
||||||
void ActivateNpad(HLERequestContext& ctx);
|
|
||||||
void DeactivateNpad(HLERequestContext& ctx);
|
|
||||||
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
|
|
||||||
void DisconnectNpad(HLERequestContext& ctx);
|
|
||||||
void GetPlayerLedPattern(HLERequestContext& ctx);
|
|
||||||
void ActivateNpadWithRevision(HLERequestContext& ctx);
|
|
||||||
void SetNpadJoyHoldType(HLERequestContext& ctx);
|
|
||||||
void GetNpadJoyHoldType(HLERequestContext& ctx);
|
|
||||||
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
|
|
||||||
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
|
|
||||||
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
|
|
||||||
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
|
|
||||||
void StartLrAssignmentMode(HLERequestContext& ctx);
|
|
||||||
void StopLrAssignmentMode(HLERequestContext& ctx);
|
|
||||||
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
|
|
||||||
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
|
|
||||||
void SwapNpadAssignment(HLERequestContext& ctx);
|
|
||||||
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
|
|
||||||
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
|
|
||||||
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
|
|
||||||
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
|
|
||||||
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
|
||||||
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
|
||||||
void GetVibrationDeviceInfo(HLERequestContext& ctx);
|
|
||||||
void SendVibrationValue(HLERequestContext& ctx);
|
|
||||||
void GetActualVibrationValue(HLERequestContext& ctx);
|
|
||||||
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
|
|
||||||
void PermitVibration(HLERequestContext& ctx);
|
|
||||||
void IsVibrationPermitted(HLERequestContext& ctx);
|
|
||||||
void SendVibrationValues(HLERequestContext& ctx);
|
|
||||||
void SendVibrationGcErmCommand(HLERequestContext& ctx);
|
|
||||||
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
|
|
||||||
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
|
||||||
void EndPermitVibrationSession(HLERequestContext& ctx);
|
|
||||||
void IsVibrationDeviceMounted(HLERequestContext& ctx);
|
|
||||||
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void StartSevenSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void StopSevenSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
|
|
||||||
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
|
|
||||||
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
|
|
||||||
void GetPalmaConnectionHandle(HLERequestContext& ctx);
|
|
||||||
void InitializePalma(HLERequestContext& ctx);
|
|
||||||
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
|
|
||||||
void GetPalmaOperationInfo(HLERequestContext& ctx);
|
|
||||||
void PlayPalmaActivity(HLERequestContext& ctx);
|
|
||||||
void SetPalmaFrModeType(HLERequestContext& ctx);
|
|
||||||
void ReadPalmaStep(HLERequestContext& ctx);
|
|
||||||
void EnablePalmaStep(HLERequestContext& ctx);
|
|
||||||
void ResetPalmaStep(HLERequestContext& ctx);
|
|
||||||
void ReadPalmaApplicationSection(HLERequestContext& ctx);
|
|
||||||
void WritePalmaApplicationSection(HLERequestContext& ctx);
|
|
||||||
void ReadPalmaUniqueCode(HLERequestContext& ctx);
|
|
||||||
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
|
|
||||||
void WritePalmaActivityEntry(HLERequestContext& ctx);
|
|
||||||
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
|
|
||||||
void WritePalmaWaveEntry(HLERequestContext& ctx);
|
|
||||||
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
|
|
||||||
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
|
|
||||||
void SuspendPalmaFeature(HLERequestContext& ctx);
|
|
||||||
void GetPalmaOperationResult(HLERequestContext& ctx);
|
|
||||||
void ReadPalmaPlayLog(HLERequestContext& ctx);
|
|
||||||
void ResetPalmaPlayLog(HLERequestContext& ctx);
|
|
||||||
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
|
|
||||||
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
|
|
||||||
void PairPalma(HLERequestContext& ctx);
|
|
||||||
void SetPalmaBoostMode(HLERequestContext& ctx);
|
|
||||||
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
|
|
||||||
void EnablePalmaBoostMode(HLERequestContext& ctx);
|
|
||||||
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
|
|
||||||
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
|
|
||||||
void SetNpadCommunicationMode(HLERequestContext& ctx);
|
|
||||||
void GetNpadCommunicationMode(HLERequestContext& ctx);
|
|
||||||
void SetTouchScreenConfiguration(HLERequestContext& ctx);
|
|
||||||
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
|
|
||||||
|
|
||||||
std::shared_ptr<IAppletResource> applet_resource;
|
|
||||||
|
|
||||||
KernelHelpers::ServiceContext service_context;
|
|
||||||
};
|
|
||||||
|
|
||||||
void LoopProcess(Core::System& system);
|
void LoopProcess(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
159
src/core/hle/service/hid/hid_debug_server.cpp
Executable file
159
src/core/hle/service/hid/hid_debug_server.cpp
Executable file
|
@ -0,0 +1,159 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/hid_debug_server.h"
|
||||||
|
#include "core/hle/service/hid/resource_manager.h"
|
||||||
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
|
||||||
|
: ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "DeactivateDebugPad"},
|
||||||
|
{1, nullptr, "SetDebugPadAutoPilotState"},
|
||||||
|
{2, nullptr, "UnsetDebugPadAutoPilotState"},
|
||||||
|
{10, nullptr, "DeactivateTouchScreen"},
|
||||||
|
{11, nullptr, "SetTouchScreenAutoPilotState"},
|
||||||
|
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
|
||||||
|
{13, nullptr, "GetTouchScreenConfiguration"},
|
||||||
|
{14, nullptr, "ProcessTouchScreenAutoTune"},
|
||||||
|
{15, nullptr, "ForceStopTouchScreenManagement"},
|
||||||
|
{16, nullptr, "ForceRestartTouchScreenManagement"},
|
||||||
|
{17, nullptr, "IsTouchScreenManaged"},
|
||||||
|
{20, nullptr, "DeactivateMouse"},
|
||||||
|
{21, nullptr, "SetMouseAutoPilotState"},
|
||||||
|
{22, nullptr, "UnsetMouseAutoPilotState"},
|
||||||
|
{25, nullptr, "SetDebugMouseAutoPilotState"},
|
||||||
|
{26, nullptr, "UnsetDebugMouseAutoPilotState"},
|
||||||
|
{30, nullptr, "DeactivateKeyboard"},
|
||||||
|
{31, nullptr, "SetKeyboardAutoPilotState"},
|
||||||
|
{32, nullptr, "UnsetKeyboardAutoPilotState"},
|
||||||
|
{50, nullptr, "DeactivateXpad"},
|
||||||
|
{51, nullptr, "SetXpadAutoPilotState"},
|
||||||
|
{52, nullptr, "UnsetXpadAutoPilotState"},
|
||||||
|
{53, nullptr, "DeactivateJoyXpad"},
|
||||||
|
{60, nullptr, "ClearNpadSystemCommonPolicy"},
|
||||||
|
{61, nullptr, "DeactivateNpad"},
|
||||||
|
{62, nullptr, "ForceDisconnectNpad"},
|
||||||
|
{91, nullptr, "DeactivateGesture"},
|
||||||
|
{110, nullptr, "DeactivateHomeButton"},
|
||||||
|
{111, nullptr, "SetHomeButtonAutoPilotState"},
|
||||||
|
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
|
||||||
|
{120, nullptr, "DeactivateSleepButton"},
|
||||||
|
{121, nullptr, "SetSleepButtonAutoPilotState"},
|
||||||
|
{122, nullptr, "UnsetSleepButtonAutoPilotState"},
|
||||||
|
{123, nullptr, "DeactivateInputDetector"},
|
||||||
|
{130, nullptr, "DeactivateCaptureButton"},
|
||||||
|
{131, nullptr, "SetCaptureButtonAutoPilotState"},
|
||||||
|
{132, nullptr, "UnsetCaptureButtonAutoPilotState"},
|
||||||
|
{133, nullptr, "SetShiftAccelerometerCalibrationValue"},
|
||||||
|
{134, nullptr, "GetShiftAccelerometerCalibrationValue"},
|
||||||
|
{135, nullptr, "SetShiftGyroscopeCalibrationValue"},
|
||||||
|
{136, nullptr, "GetShiftGyroscopeCalibrationValue"},
|
||||||
|
{140, nullptr, "DeactivateConsoleSixAxisSensor"},
|
||||||
|
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
|
||||||
|
{142, nullptr, "DeactivateSevenSixAxisSensor"},
|
||||||
|
{143, nullptr, "GetConsoleSixAxisSensorCountStates"},
|
||||||
|
{144, nullptr, "GetAccelerometerFsr"},
|
||||||
|
{145, nullptr, "SetAccelerometerFsr"},
|
||||||
|
{146, nullptr, "GetAccelerometerOdr"},
|
||||||
|
{147, nullptr, "SetAccelerometerOdr"},
|
||||||
|
{148, nullptr, "GetGyroscopeFsr"},
|
||||||
|
{149, nullptr, "SetGyroscopeFsr"},
|
||||||
|
{150, nullptr, "GetGyroscopeOdr"},
|
||||||
|
{151, nullptr, "SetGyroscopeOdr"},
|
||||||
|
{152, nullptr, "GetWhoAmI"},
|
||||||
|
{201, nullptr, "ActivateFirmwareUpdate"},
|
||||||
|
{202, nullptr, "DeactivateFirmwareUpdate"},
|
||||||
|
{203, nullptr, "StartFirmwareUpdate"},
|
||||||
|
{204, nullptr, "GetFirmwareUpdateStage"},
|
||||||
|
{205, nullptr, "GetFirmwareVersion"},
|
||||||
|
{206, nullptr, "GetDestinationFirmwareVersion"},
|
||||||
|
{207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
|
||||||
|
{208, nullptr, "StartFirmwareUpdateForRevert"},
|
||||||
|
{209, nullptr, "GetAvailableFirmwareVersionForRevert"},
|
||||||
|
{210, nullptr, "IsFirmwareUpdatingDevice"},
|
||||||
|
{211, nullptr, "StartFirmwareUpdateIndividual"},
|
||||||
|
{215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
|
||||||
|
{216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
|
||||||
|
{221, nullptr, "UpdateControllerColor"},
|
||||||
|
{222, nullptr, "ConnectUsbPadsAsync"},
|
||||||
|
{223, nullptr, "DisconnectUsbPadsAsync"},
|
||||||
|
{224, nullptr, "UpdateDesignInfo"},
|
||||||
|
{225, nullptr, "GetUniquePadDriverState"},
|
||||||
|
{226, nullptr, "GetSixAxisSensorDriverStates"},
|
||||||
|
{227, nullptr, "GetRxPacketHistory"},
|
||||||
|
{228, nullptr, "AcquireOperationEventHandle"},
|
||||||
|
{229, nullptr, "ReadSerialFlash"},
|
||||||
|
{230, nullptr, "WriteSerialFlash"},
|
||||||
|
{231, nullptr, "GetOperationResult"},
|
||||||
|
{232, nullptr, "EnableShipmentMode"},
|
||||||
|
{233, nullptr, "ClearPairingInfo"},
|
||||||
|
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
|
||||||
|
{235, nullptr, "EnableAnalogStickPower"},
|
||||||
|
{236, nullptr, "RequestKuinaUartClockCal"},
|
||||||
|
{237, nullptr, "GetKuinaUartClockCal"},
|
||||||
|
{238, nullptr, "SetKuinaUartClockTrim"},
|
||||||
|
{239, nullptr, "KuinaLoopbackTest"},
|
||||||
|
{240, nullptr, "RequestBatteryVoltage"},
|
||||||
|
{241, nullptr, "GetBatteryVoltage"},
|
||||||
|
{242, nullptr, "GetUniquePadPowerInfo"},
|
||||||
|
{243, nullptr, "RebootUniquePad"},
|
||||||
|
{244, nullptr, "RequestKuinaFirmwareVersion"},
|
||||||
|
{245, nullptr, "GetKuinaFirmwareVersion"},
|
||||||
|
{246, nullptr, "GetVidPid"},
|
||||||
|
{247, nullptr, "GetAnalogStickCalibrationValue"},
|
||||||
|
{248, nullptr, "GetUniquePadIdsFull"},
|
||||||
|
{249, nullptr, "ConnectUniquePad"},
|
||||||
|
{250, nullptr, "IsVirtual"},
|
||||||
|
{251, nullptr, "GetAnalogStickModuleParam"},
|
||||||
|
{301, nullptr, "GetAbstractedPadHandles"},
|
||||||
|
{302, nullptr, "GetAbstractedPadState"},
|
||||||
|
{303, nullptr, "GetAbstractedPadsState"},
|
||||||
|
{321, nullptr, "SetAutoPilotVirtualPadState"},
|
||||||
|
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
|
||||||
|
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
|
||||||
|
{324, nullptr, "AttachHdlsWorkBuffer"},
|
||||||
|
{325, nullptr, "ReleaseHdlsWorkBuffer"},
|
||||||
|
{326, nullptr, "DumpHdlsNpadAssignmentState"},
|
||||||
|
{327, nullptr, "DumpHdlsStates"},
|
||||||
|
{328, nullptr, "ApplyHdlsNpadAssignmentState"},
|
||||||
|
{329, nullptr, "ApplyHdlsStateList"},
|
||||||
|
{330, nullptr, "AttachHdlsVirtualDevice"},
|
||||||
|
{331, nullptr, "DetachHdlsVirtualDevice"},
|
||||||
|
{332, nullptr, "SetHdlsState"},
|
||||||
|
{350, nullptr, "AddRegisteredDevice"},
|
||||||
|
{400, nullptr, "DisableExternalMcuOnNxDevice"},
|
||||||
|
{401, nullptr, "DisableRailDeviceFiltering"},
|
||||||
|
{402, nullptr, "EnableWiredPairing"},
|
||||||
|
{403, nullptr, "EnableShipmentModeAutoClear"},
|
||||||
|
{404, nullptr, "SetRailEnabled"},
|
||||||
|
{500, nullptr, "SetFactoryInt"},
|
||||||
|
{501, nullptr, "IsFactoryBootEnabled"},
|
||||||
|
{550, nullptr, "SetAnalogStickModelDataTemporarily"},
|
||||||
|
{551, nullptr, "GetAnalogStickModelData"},
|
||||||
|
{552, nullptr, "ResetAnalogStickModelData"},
|
||||||
|
{600, nullptr, "ConvertPadState"},
|
||||||
|
{650, nullptr, "AddButtonPlayData"},
|
||||||
|
{651, nullptr, "StartButtonPlayData"},
|
||||||
|
{652, nullptr, "StopButtonPlayData"},
|
||||||
|
{2000, nullptr, "DeactivateDigitizer"},
|
||||||
|
{2001, nullptr, "SetDigitizerAutoPilotState"},
|
||||||
|
{2002, nullptr, "UnsetDigitizerAutoPilotState"},
|
||||||
|
{2002, nullptr, "ReloadFirmwareDebugSettings"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
IHidDebugServer::~IHidDebugServer() = default;
|
||||||
|
|
||||||
|
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
|
||||||
|
resource_manager->Initialize();
|
||||||
|
return resource_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
26
src/core/hle/service/hid/hid_debug_server.h
Executable file
26
src/core/hle/service/hid/hid_debug_server.h
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
class ResourceManager;
|
||||||
|
|
||||||
|
class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
|
||||||
|
public:
|
||||||
|
explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
||||||
|
~IHidDebugServer() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<ResourceManager> GetResourceManager();
|
||||||
|
|
||||||
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
2269
src/core/hle/service/hid/hid_server.cpp
Executable file
2269
src/core/hle/service/hid/hid_server.cpp
Executable file
File diff suppressed because it is too large
Load diff
138
src/core/hle/service/hid/hid_server.h
Executable file
138
src/core/hle/service/hid/hid_server.h
Executable file
|
@ -0,0 +1,138 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
class ResourceManager;
|
||||||
|
|
||||||
|
class IHidServer final : public ServiceFramework<IHidServer> {
|
||||||
|
public:
|
||||||
|
explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
||||||
|
~IHidServer() override;
|
||||||
|
|
||||||
|
std::shared_ptr<ResourceManager> GetResourceManager();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateAppletResource(HLERequestContext& ctx);
|
||||||
|
void ActivateDebugPad(HLERequestContext& ctx);
|
||||||
|
void ActivateTouchScreen(HLERequestContext& ctx);
|
||||||
|
void ActivateMouse(HLERequestContext& ctx);
|
||||||
|
void ActivateKeyboard(HLERequestContext& ctx);
|
||||||
|
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
|
||||||
|
void ActivateXpad(HLERequestContext& ctx);
|
||||||
|
void GetXpadIds(HLERequestContext& ctx);
|
||||||
|
void GetJoyXpadIds(HLERequestContext& ctx);
|
||||||
|
void ActivateSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void DeactivateSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void StartSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void StopSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
|
||||||
|
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
|
||||||
|
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
||||||
|
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
||||||
|
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
||||||
|
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
||||||
|
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
||||||
|
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
||||||
|
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
|
||||||
|
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
|
||||||
|
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
|
||||||
|
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
|
||||||
|
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
|
||||||
|
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
|
||||||
|
void ActivateGesture(HLERequestContext& ctx);
|
||||||
|
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
|
||||||
|
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
|
||||||
|
void SetSupportedNpadIdType(HLERequestContext& ctx);
|
||||||
|
void ActivateNpad(HLERequestContext& ctx);
|
||||||
|
void DeactivateNpad(HLERequestContext& ctx);
|
||||||
|
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
|
||||||
|
void DisconnectNpad(HLERequestContext& ctx);
|
||||||
|
void GetPlayerLedPattern(HLERequestContext& ctx);
|
||||||
|
void ActivateNpadWithRevision(HLERequestContext& ctx);
|
||||||
|
void SetNpadJoyHoldType(HLERequestContext& ctx);
|
||||||
|
void GetNpadJoyHoldType(HLERequestContext& ctx);
|
||||||
|
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
|
||||||
|
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
|
||||||
|
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
|
||||||
|
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
|
||||||
|
void StartLrAssignmentMode(HLERequestContext& ctx);
|
||||||
|
void StopLrAssignmentMode(HLERequestContext& ctx);
|
||||||
|
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
|
||||||
|
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
|
||||||
|
void SwapNpadAssignment(HLERequestContext& ctx);
|
||||||
|
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
|
||||||
|
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
|
||||||
|
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
|
||||||
|
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
|
||||||
|
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
||||||
|
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
||||||
|
void GetVibrationDeviceInfo(HLERequestContext& ctx);
|
||||||
|
void SendVibrationValue(HLERequestContext& ctx);
|
||||||
|
void GetActualVibrationValue(HLERequestContext& ctx);
|
||||||
|
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
|
||||||
|
void PermitVibration(HLERequestContext& ctx);
|
||||||
|
void IsVibrationPermitted(HLERequestContext& ctx);
|
||||||
|
void SendVibrationValues(HLERequestContext& ctx);
|
||||||
|
void SendVibrationGcErmCommand(HLERequestContext& ctx);
|
||||||
|
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
|
||||||
|
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
||||||
|
void EndPermitVibrationSession(HLERequestContext& ctx);
|
||||||
|
void IsVibrationDeviceMounted(HLERequestContext& ctx);
|
||||||
|
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void StartSevenSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void StopSevenSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
|
||||||
|
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
|
||||||
|
void GetPalmaConnectionHandle(HLERequestContext& ctx);
|
||||||
|
void InitializePalma(HLERequestContext& ctx);
|
||||||
|
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
|
||||||
|
void GetPalmaOperationInfo(HLERequestContext& ctx);
|
||||||
|
void PlayPalmaActivity(HLERequestContext& ctx);
|
||||||
|
void SetPalmaFrModeType(HLERequestContext& ctx);
|
||||||
|
void ReadPalmaStep(HLERequestContext& ctx);
|
||||||
|
void EnablePalmaStep(HLERequestContext& ctx);
|
||||||
|
void ResetPalmaStep(HLERequestContext& ctx);
|
||||||
|
void ReadPalmaApplicationSection(HLERequestContext& ctx);
|
||||||
|
void WritePalmaApplicationSection(HLERequestContext& ctx);
|
||||||
|
void ReadPalmaUniqueCode(HLERequestContext& ctx);
|
||||||
|
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
|
||||||
|
void WritePalmaActivityEntry(HLERequestContext& ctx);
|
||||||
|
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
|
||||||
|
void WritePalmaWaveEntry(HLERequestContext& ctx);
|
||||||
|
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
|
||||||
|
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
|
||||||
|
void SuspendPalmaFeature(HLERequestContext& ctx);
|
||||||
|
void GetPalmaOperationResult(HLERequestContext& ctx);
|
||||||
|
void ReadPalmaPlayLog(HLERequestContext& ctx);
|
||||||
|
void ResetPalmaPlayLog(HLERequestContext& ctx);
|
||||||
|
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
|
||||||
|
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
|
||||||
|
void PairPalma(HLERequestContext& ctx);
|
||||||
|
void SetPalmaBoostMode(HLERequestContext& ctx);
|
||||||
|
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
|
||||||
|
void EnablePalmaBoostMode(HLERequestContext& ctx);
|
||||||
|
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
|
||||||
|
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
|
||||||
|
void SetNpadCommunicationMode(HLERequestContext& ctx);
|
||||||
|
void GetNpadCommunicationMode(HLERequestContext& ctx);
|
||||||
|
void SetTouchScreenConfiguration(HLERequestContext& ctx);
|
||||||
|
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
304
src/core/hle/service/hid/hid_system_server.cpp
Executable file
304
src/core/hle/service/hid/hid_system_server.cpp
Executable file
|
@ -0,0 +1,304 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
|
#include "core/hle/service/hid/controllers/touchscreen.h"
|
||||||
|
#include "core/hle/service/hid/errors.h"
|
||||||
|
#include "core/hle/service/hid/hid_system_server.h"
|
||||||
|
#include "core/hle/service/hid/resource_manager.h"
|
||||||
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
|
||||||
|
: ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
|
||||||
|
resource_manager{resource} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{31, nullptr, "SendKeyboardLockKeyEvent"},
|
||||||
|
{101, nullptr, "AcquireHomeButtonEventHandle"},
|
||||||
|
{111, nullptr, "ActivateHomeButton"},
|
||||||
|
{121, nullptr, "AcquireSleepButtonEventHandle"},
|
||||||
|
{131, nullptr, "ActivateSleepButton"},
|
||||||
|
{141, nullptr, "AcquireCaptureButtonEventHandle"},
|
||||||
|
{151, nullptr, "ActivateCaptureButton"},
|
||||||
|
{161, nullptr, "GetPlatformConfig"},
|
||||||
|
{210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
|
||||||
|
{211, nullptr, "GetNpadsWithNfc"},
|
||||||
|
{212, nullptr, "AcquireNfcActivateEventHandle"},
|
||||||
|
{213, nullptr, "ActivateNfc"},
|
||||||
|
{214, nullptr, "GetXcdHandleForNpadWithNfc"},
|
||||||
|
{215, nullptr, "IsNfcActivated"},
|
||||||
|
{230, nullptr, "AcquireIrSensorEventHandle"},
|
||||||
|
{231, nullptr, "ActivateIrSensor"},
|
||||||
|
{232, nullptr, "GetIrSensorState"},
|
||||||
|
{233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
|
||||||
|
{301, nullptr, "ActivateNpadSystem"},
|
||||||
|
{303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
|
||||||
|
{304, nullptr, "EnableAssigningSingleOnSlSrPress"},
|
||||||
|
{305, nullptr, "DisableAssigningSingleOnSlSrPress"},
|
||||||
|
{306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
|
||||||
|
{307, nullptr, "GetNpadSystemExtStyle"},
|
||||||
|
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
|
||||||
|
{309, nullptr, "GetNpadFullKeyGripColor"},
|
||||||
|
{310, nullptr, "GetMaskedSupportedNpadStyleSet"},
|
||||||
|
{311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
|
||||||
|
{312, nullptr, "SetSupportedNpadStyleSetAll"},
|
||||||
|
{313, nullptr, "GetNpadCaptureButtonAssignment"},
|
||||||
|
{314, nullptr, "GetAppletFooterUiType"},
|
||||||
|
{315, nullptr, "GetAppletDetailedUiType"},
|
||||||
|
{316, nullptr, "GetNpadInterfaceType"},
|
||||||
|
{317, nullptr, "GetNpadLeftRightInterfaceType"},
|
||||||
|
{318, nullptr, "HasBattery"},
|
||||||
|
{319, nullptr, "HasLeftRightBattery"},
|
||||||
|
{321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
|
||||||
|
{322, nullptr, "GetIrSensorState"},
|
||||||
|
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
|
||||||
|
{324, nullptr, "GetUniquePadButtonSet"},
|
||||||
|
{325, nullptr, "GetUniquePadColor"},
|
||||||
|
{326, nullptr, "GetUniquePadAppletDetailedUiType"},
|
||||||
|
{327, nullptr, "GetAbstractedPadIdDataFromNpad"},
|
||||||
|
{328, nullptr, "AttachAbstractedPadToNpad"},
|
||||||
|
{329, nullptr, "DetachAbstractedPadAll"},
|
||||||
|
{330, nullptr, "CheckAbstractedPadConnection"},
|
||||||
|
{500, nullptr, "SetAppletResourceUserId"},
|
||||||
|
{501, nullptr, "RegisterAppletResourceUserId"},
|
||||||
|
{502, nullptr, "UnregisterAppletResourceUserId"},
|
||||||
|
{503, nullptr, "EnableAppletToGetInput"},
|
||||||
|
{504, nullptr, "SetAruidValidForVibration"},
|
||||||
|
{505, nullptr, "EnableAppletToGetSixAxisSensor"},
|
||||||
|
{506, nullptr, "EnableAppletToGetPadInput"},
|
||||||
|
{507, nullptr, "EnableAppletToGetTouchScreen"},
|
||||||
|
{510, nullptr, "SetVibrationMasterVolume"},
|
||||||
|
{511, nullptr, "GetVibrationMasterVolume"},
|
||||||
|
{512, nullptr, "BeginPermitVibrationSession"},
|
||||||
|
{513, nullptr, "EndPermitVibrationSession"},
|
||||||
|
{514, nullptr, "Unknown514"},
|
||||||
|
{520, nullptr, "EnableHandheldHids"},
|
||||||
|
{521, nullptr, "DisableHandheldHids"},
|
||||||
|
{522, nullptr, "SetJoyConRailEnabled"},
|
||||||
|
{523, nullptr, "IsJoyConRailEnabled"},
|
||||||
|
{524, nullptr, "IsHandheldHidsEnabled"},
|
||||||
|
{525, nullptr, "IsJoyConAttachedOnAllRail"},
|
||||||
|
{540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
|
||||||
|
{541, nullptr, "GetPlayReportControllerUsages"},
|
||||||
|
{542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
|
||||||
|
{543, nullptr, "GetRegisteredDevicesOld"},
|
||||||
|
{544, nullptr, "AcquireConnectionTriggerTimeoutEvent"},
|
||||||
|
{545, nullptr, "SendConnectionTrigger"},
|
||||||
|
{546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
|
||||||
|
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||||
|
{548, nullptr, "GetRegisteredDevices"},
|
||||||
|
{549, nullptr, "GetConnectableRegisteredDevices"},
|
||||||
|
{700, nullptr, "ActivateUniquePad"},
|
||||||
|
{702, nullptr, "AcquireUniquePadConnectionEventHandle"},
|
||||||
|
{703, nullptr, "GetUniquePadIds"},
|
||||||
|
{751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
|
||||||
|
{800, nullptr, "ListSixAxisSensorHandles"},
|
||||||
|
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
|
||||||
|
{802, nullptr, "ResetSixAxisSensorCalibrationValues"},
|
||||||
|
{803, nullptr, "StartSixAxisSensorUserCalibration"},
|
||||||
|
{804, nullptr, "CancelSixAxisSensorUserCalibration"},
|
||||||
|
{805, nullptr, "GetUniquePadBluetoothAddress"},
|
||||||
|
{806, nullptr, "DisconnectUniquePad"},
|
||||||
|
{807, nullptr, "GetUniquePadType"},
|
||||||
|
{808, nullptr, "GetUniquePadInterface"},
|
||||||
|
{809, nullptr, "GetUniquePadSerialNumber"},
|
||||||
|
{810, nullptr, "GetUniquePadControllerNumber"},
|
||||||
|
{811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
|
||||||
|
{812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
|
||||||
|
{821, nullptr, "StartAnalogStickManualCalibration"},
|
||||||
|
{822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
|
||||||
|
{823, nullptr, "CancelAnalogStickManualCalibration"},
|
||||||
|
{824, nullptr, "ResetAnalogStickManualCalibration"},
|
||||||
|
{825, nullptr, "GetAnalogStickState"},
|
||||||
|
{826, nullptr, "GetAnalogStickManualCalibrationStage"},
|
||||||
|
{827, nullptr, "IsAnalogStickButtonPressed"},
|
||||||
|
{828, nullptr, "IsAnalogStickInReleasePosition"},
|
||||||
|
{829, nullptr, "IsAnalogStickInCircumference"},
|
||||||
|
{830, nullptr, "SetNotificationLedPattern"},
|
||||||
|
{831, nullptr, "SetNotificationLedPatternWithTimeout"},
|
||||||
|
{832, nullptr, "PrepareHidsForNotificationWake"},
|
||||||
|
{850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
|
||||||
|
{851, nullptr, "EnableUsbFullKeyController"},
|
||||||
|
{852, nullptr, "IsUsbConnected"},
|
||||||
|
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
|
||||||
|
{900, nullptr, "ActivateInputDetector"},
|
||||||
|
{901, nullptr, "NotifyInputDetector"},
|
||||||
|
{1000, nullptr, "InitializeFirmwareUpdate"},
|
||||||
|
{1001, nullptr, "GetFirmwareVersion"},
|
||||||
|
{1002, nullptr, "GetAvailableFirmwareVersion"},
|
||||||
|
{1003, nullptr, "IsFirmwareUpdateAvailable"},
|
||||||
|
{1004, nullptr, "CheckFirmwareUpdateRequired"},
|
||||||
|
{1005, nullptr, "StartFirmwareUpdate"},
|
||||||
|
{1006, nullptr, "AbortFirmwareUpdate"},
|
||||||
|
{1007, nullptr, "GetFirmwareUpdateState"},
|
||||||
|
{1008, nullptr, "ActivateAudioControl"},
|
||||||
|
{1009, nullptr, "AcquireAudioControlEventHandle"},
|
||||||
|
{1010, nullptr, "GetAudioControlStates"},
|
||||||
|
{1011, nullptr, "DeactivateAudioControl"},
|
||||||
|
{1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
|
||||||
|
{1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
|
||||||
|
{1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
|
||||||
|
{1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
|
||||||
|
{1100, nullptr, "GetHidbusSystemServiceObject"},
|
||||||
|
{1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
|
||||||
|
{1130, nullptr, "InitializeUsbFirmwareUpdate"},
|
||||||
|
{1131, nullptr, "FinalizeUsbFirmwareUpdate"},
|
||||||
|
{1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
|
||||||
|
{1133, nullptr, "StartUsbFirmwareUpdate"},
|
||||||
|
{1134, nullptr, "GetUsbFirmwareUpdateState"},
|
||||||
|
{1150, nullptr, "SetTouchScreenMagnification"},
|
||||||
|
{1151, nullptr, "GetTouchScreenFirmwareVersion"},
|
||||||
|
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
|
||||||
|
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
|
||||||
|
{1154, nullptr, "IsFirmwareAvailableForNotification"},
|
||||||
|
{1155, nullptr, "SetForceHandheldStyleVibration"},
|
||||||
|
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
|
||||||
|
{1157, nullptr, "CancelConnectionTrigger"},
|
||||||
|
{1200, nullptr, "IsButtonConfigSupported"},
|
||||||
|
{1201, nullptr, "IsButtonConfigEmbeddedSupported"},
|
||||||
|
{1202, nullptr, "DeleteButtonConfig"},
|
||||||
|
{1203, nullptr, "DeleteButtonConfigEmbedded"},
|
||||||
|
{1204, nullptr, "SetButtonConfigEnabled"},
|
||||||
|
{1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
|
||||||
|
{1206, nullptr, "IsButtonConfigEnabled"},
|
||||||
|
{1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
|
||||||
|
{1208, nullptr, "SetButtonConfigEmbedded"},
|
||||||
|
{1209, nullptr, "SetButtonConfigFull"},
|
||||||
|
{1210, nullptr, "SetButtonConfigLeft"},
|
||||||
|
{1211, nullptr, "SetButtonConfigRight"},
|
||||||
|
{1212, nullptr, "GetButtonConfigEmbedded"},
|
||||||
|
{1213, nullptr, "GetButtonConfigFull"},
|
||||||
|
{1214, nullptr, "GetButtonConfigLeft"},
|
||||||
|
{1215, nullptr, "GetButtonConfigRight"},
|
||||||
|
{1250, nullptr, "IsCustomButtonConfigSupported"},
|
||||||
|
{1251, nullptr, "IsDefaultButtonConfigEmbedded"},
|
||||||
|
{1252, nullptr, "IsDefaultButtonConfigFull"},
|
||||||
|
{1253, nullptr, "IsDefaultButtonConfigLeft"},
|
||||||
|
{1254, nullptr, "IsDefaultButtonConfigRight"},
|
||||||
|
{1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
|
||||||
|
{1256, nullptr, "IsButtonConfigStorageFullEmpty"},
|
||||||
|
{1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
|
||||||
|
{1258, nullptr, "IsButtonConfigStorageRightEmpty"},
|
||||||
|
{1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
|
||||||
|
{1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
|
||||||
|
{1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
|
||||||
|
{1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
|
||||||
|
{1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
|
||||||
|
{1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
|
||||||
|
{1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
|
||||||
|
{1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
|
||||||
|
{1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
|
||||||
|
{1268, nullptr, "DeleteButtonConfigStorageFull"},
|
||||||
|
{1269, nullptr, "DeleteButtonConfigStorageLeft"},
|
||||||
|
{1270, nullptr, "DeleteButtonConfigStorageRight"},
|
||||||
|
{1271, nullptr, "IsUsingCustomButtonConfig"},
|
||||||
|
{1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
|
||||||
|
{1273, nullptr, "SetAllCustomButtonConfigEnabled"},
|
||||||
|
{1274, nullptr, "SetDefaultButtonConfig"},
|
||||||
|
{1275, nullptr, "SetAllDefaultButtonConfig"},
|
||||||
|
{1276, nullptr, "SetHidButtonConfigEmbedded"},
|
||||||
|
{1277, nullptr, "SetHidButtonConfigFull"},
|
||||||
|
{1278, nullptr, "SetHidButtonConfigLeft"},
|
||||||
|
{1279, nullptr, "SetHidButtonConfigRight"},
|
||||||
|
{1280, nullptr, "GetHidButtonConfigEmbedded"},
|
||||||
|
{1281, nullptr, "GetHidButtonConfigFull"},
|
||||||
|
{1282, nullptr, "GetHidButtonConfigLeft"},
|
||||||
|
{1283, nullptr, "GetHidButtonConfigRight"},
|
||||||
|
{1284, nullptr, "GetButtonConfigStorageEmbedded"},
|
||||||
|
{1285, nullptr, "GetButtonConfigStorageFull"},
|
||||||
|
{1286, nullptr, "GetButtonConfigStorageLeft"},
|
||||||
|
{1287, nullptr, "GetButtonConfigStorageRight"},
|
||||||
|
{1288, nullptr, "SetButtonConfigStorageEmbedded"},
|
||||||
|
{1289, nullptr, "SetButtonConfigStorageFull"},
|
||||||
|
{1290, nullptr, "DeleteButtonConfigStorageRight"},
|
||||||
|
{1291, nullptr, "DeleteButtonConfigStorageRight"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
|
joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
|
||||||
|
}
|
||||||
|
|
||||||
|
IHidSystemServer::~IHidSystemServer() {
|
||||||
|
service_context.CloseEvent(joy_detach_event);
|
||||||
|
};
|
||||||
|
|
||||||
|
void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_HID, "called");
|
||||||
|
|
||||||
|
GetResourceManager()
|
||||||
|
->GetController<Controller_NPad>(HidController::NPad)
|
||||||
|
.ApplyNpadSystemCommonPolicy();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_HID, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushEnum(system.HIDCore().GetLastActiveController());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
|
||||||
|
|
||||||
|
const std::vector<Core::HID::UniquePadId> unique_pads{};
|
||||||
|
|
||||||
|
ctx.WriteBuffer(unique_pads);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(static_cast<u32>(unique_pads.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_AM, "called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
|
||||||
|
const bool is_enabled = false;
|
||||||
|
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(is_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||||
|
|
||||||
|
Core::HID::TouchScreenConfigurationForNx touchscreen_config{
|
||||||
|
.mode = Core::HID::TouchScreenModeForNx::Finger,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
|
||||||
|
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
|
||||||
|
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushRaw(touchscreen_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
|
||||||
|
resource_manager->Initialize();
|
||||||
|
return resource_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
40
src/core/hle/service/hid/hid_system_server.h
Executable file
40
src/core/hle/service/hid/hid_system_server.h
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
class ResourceManager;
|
||||||
|
|
||||||
|
class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
|
||||||
|
public:
|
||||||
|
explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
||||||
|
~IHidSystemServer() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
|
||||||
|
void GetLastActiveNpad(HLERequestContext& ctx);
|
||||||
|
void GetUniquePadsFromNpad(HLERequestContext& ctx);
|
||||||
|
void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
|
||||||
|
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
|
||||||
|
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
std::shared_ptr<ResourceManager> GetResourceManager();
|
||||||
|
|
||||||
|
Kernel::KEvent* joy_detach_event;
|
||||||
|
KernelHelpers::ServiceContext service_context;
|
||||||
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -3,15 +3,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
#include "core/hid/irs_types.h"
|
#include "core/hid/irs_types.h"
|
||||||
#include "core/hle/service/hid/irsensor/processor_base.h"
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class System;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core::HID {
|
||||||
class EmulatedController;
|
class EmulatedController;
|
||||||
} // namespace Core::HID
|
} // namespace Core::HID
|
||||||
|
|
192
src/core/hle/service/hid/resource_manager.cpp
Executable file
192
src/core/hle/service/hid/resource_manager.cpp
Executable file
|
@ -0,0 +1,192 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
|
#include "core/hle/service/hid/resource_manager.h"
|
||||||
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/controllers/console_sixaxis.h"
|
||||||
|
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||||
|
#include "core/hle/service/hid/controllers/debug_pad.h"
|
||||||
|
#include "core/hle/service/hid/controllers/gesture.h"
|
||||||
|
#include "core/hle/service/hid/controllers/keyboard.h"
|
||||||
|
#include "core/hle/service/hid/controllers/mouse.h"
|
||||||
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
|
#include "core/hle/service/hid/controllers/palma.h"
|
||||||
|
#include "core/hle/service/hid/controllers/stubbed.h"
|
||||||
|
#include "core/hle/service/hid/controllers/touchscreen.h"
|
||||||
|
#include "core/hle/service/hid/controllers/xpad.h"
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
// Updating period for each HID device.
|
||||||
|
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
|
||||||
|
// Correct npad_update_ns is 4ms this is overclocked to lower input lag
|
||||||
|
constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
|
||||||
|
constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
|
||||||
|
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
||||||
|
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
||||||
|
|
||||||
|
ResourceManager::ResourceManager(Core::System& system_)
|
||||||
|
: system{system_}, service_context{system_, "hid"} {}
|
||||||
|
|
||||||
|
ResourceManager::~ResourceManager() = default;
|
||||||
|
|
||||||
|
void ResourceManager::Initialize() {
|
||||||
|
if (is_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
|
||||||
|
MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory);
|
||||||
|
MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory);
|
||||||
|
MakeController<Controller_Mouse>(HidController::Mouse, shared_memory);
|
||||||
|
MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory);
|
||||||
|
MakeController<Controller_XPad>(HidController::XPad, shared_memory);
|
||||||
|
MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory);
|
||||||
|
MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory);
|
||||||
|
MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory);
|
||||||
|
MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory);
|
||||||
|
MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory);
|
||||||
|
MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
|
||||||
|
MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
|
||||||
|
MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
|
||||||
|
MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory);
|
||||||
|
MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
|
||||||
|
|
||||||
|
// Homebrew doesn't try to activate some controllers, so we activate them by default
|
||||||
|
GetController<Controller_NPad>(HidController::NPad).ActivateController();
|
||||||
|
GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
|
||||||
|
|
||||||
|
GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
|
||||||
|
GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
|
||||||
|
GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
|
||||||
|
GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
|
||||||
|
GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
|
||||||
|
GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00);
|
||||||
|
|
||||||
|
system.HIDCore().ReloadInputDevices();
|
||||||
|
is_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::ActivateController(HidController controller) {
|
||||||
|
controllers[static_cast<size_t>(controller)]->ActivateController();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::DeactivateController(HidController controller) {
|
||||||
|
controllers[static_cast<size_t>(controller)]->DeactivateController();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::UpdateControllers(std::uintptr_t user_data,
|
||||||
|
std::chrono::nanoseconds ns_late) {
|
||||||
|
auto& core_timing = system.CoreTiming();
|
||||||
|
|
||||||
|
for (const auto& controller : controllers) {
|
||||||
|
// Keyboard has it's own update event
|
||||||
|
if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Mouse has it's own update event
|
||||||
|
if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Npad has it's own update event
|
||||||
|
if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
controller->OnUpdate(core_timing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||||
|
auto& core_timing = system.CoreTiming();
|
||||||
|
|
||||||
|
controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
|
||||||
|
std::chrono::nanoseconds ns_late) {
|
||||||
|
auto& core_timing = system.CoreTiming();
|
||||||
|
|
||||||
|
controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
|
||||||
|
controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||||
|
auto& core_timing = system.CoreTiming();
|
||||||
|
|
||||||
|
controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
|
||||||
|
: ServiceFramework{system_, "IAppletResource"} {
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
|
||||||
|
};
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
|
resource->Initialize();
|
||||||
|
|
||||||
|
// Register update callbacks
|
||||||
|
npad_update_event = Core::Timing::CreateEvent(
|
||||||
|
"HID::UpdatePadCallback",
|
||||||
|
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||||
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
|
const auto guard = LockService();
|
||||||
|
resource->UpdateNpad(user_data, ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
default_update_event = Core::Timing::CreateEvent(
|
||||||
|
"HID::UpdateDefaultCallback",
|
||||||
|
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||||
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
|
const auto guard = LockService();
|
||||||
|
resource->UpdateControllers(user_data, ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
mouse_keyboard_update_event = Core::Timing::CreateEvent(
|
||||||
|
"HID::UpdateMouseKeyboardCallback",
|
||||||
|
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||||
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
|
const auto guard = LockService();
|
||||||
|
resource->UpdateMouseKeyboard(user_data, ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
motion_update_event = Core::Timing::CreateEvent(
|
||||||
|
"HID::UpdateMotionCallback",
|
||||||
|
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
||||||
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
|
const auto guard = LockService();
|
||||||
|
resource->UpdateMotion(user_data, ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
|
system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
|
||||||
|
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
|
||||||
|
default_update_event);
|
||||||
|
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
|
||||||
|
mouse_keyboard_update_event);
|
||||||
|
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
|
||||||
|
motion_update_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
IAppletResource::~IAppletResource() {
|
||||||
|
system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
|
||||||
|
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
||||||
|
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
||||||
|
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_HID, "called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
106
src/core/hle/service/hid/resource_manager.h
Executable file
106
src/core/hle/service/hid/resource_manager.h
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||||
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
struct EventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Core::HID {
|
||||||
|
class HIDCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::HID {
|
||||||
|
|
||||||
|
enum class HidController : std::size_t {
|
||||||
|
DebugPad,
|
||||||
|
Touchscreen,
|
||||||
|
Mouse,
|
||||||
|
Keyboard,
|
||||||
|
XPad,
|
||||||
|
HomeButton,
|
||||||
|
SleepButton,
|
||||||
|
CaptureButton,
|
||||||
|
InputDetector,
|
||||||
|
UniquePad,
|
||||||
|
NPad,
|
||||||
|
Gesture,
|
||||||
|
ConsoleSixAxisSensor,
|
||||||
|
DebugMouse,
|
||||||
|
Palma,
|
||||||
|
|
||||||
|
MaxControllers,
|
||||||
|
};
|
||||||
|
class ResourceManager {
|
||||||
|
public:
|
||||||
|
explicit ResourceManager(Core::System& system_);
|
||||||
|
~ResourceManager();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& GetController(HidController controller) {
|
||||||
|
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& GetController(HidController controller) const {
|
||||||
|
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
void ActivateController(HidController controller);
|
||||||
|
void DeactivateController(HidController controller);
|
||||||
|
|
||||||
|
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||||
|
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||||
|
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||||
|
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
void MakeController(HidController controller, u8* shared_memory) {
|
||||||
|
if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
|
||||||
|
controllers[static_cast<std::size_t>(controller)] =
|
||||||
|
std::make_unique<T>(system, shared_memory);
|
||||||
|
} else {
|
||||||
|
controllers[static_cast<std::size_t>(controller)] =
|
||||||
|
std::make_unique<T>(system.HIDCore(), shared_memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
|
||||||
|
controllers[static_cast<std::size_t>(controller)] =
|
||||||
|
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_initialized{false};
|
||||||
|
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
||||||
|
controllers{};
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
KernelHelpers::ServiceContext service_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||||
|
public:
|
||||||
|
explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
||||||
|
~IAppletResource() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void GetSharedMemoryHandle(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
std::shared_ptr<Core::Timing::EventType> npad_update_event;
|
||||||
|
std::shared_ptr<Core::Timing::EventType> default_update_event;
|
||||||
|
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
||||||
|
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::HID
|
|
@ -10,7 +10,8 @@
|
||||||
#include "core/hle/kernel/k_page_table.h"
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/service/hid/controllers/npad.h"
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
#include "core/hle/service/hid/hid.h"
|
#include "core/hle/service/hid/hid_server.h"
|
||||||
|
#include "core/hle/service/hid/resource_manager.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/memory/cheat_engine.h"
|
#include "core/memory/cheat_engine.h"
|
||||||
|
@ -54,13 +55,13 @@ void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size)
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 StandardVmCallbacks::HidKeysDown() {
|
u64 StandardVmCallbacks::HidKeysDown() {
|
||||||
const auto hid = system.ServiceManager().GetService<Service::HID::Hid>("hid");
|
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
|
||||||
if (hid == nullptr) {
|
if (hid == nullptr) {
|
||||||
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
|
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto applet_resource = hid->GetAppletResource();
|
const auto applet_resource = hid->GetResourceManager();
|
||||||
if (applet_resource == nullptr) {
|
if (applet_resource == nullptr) {
|
||||||
LOG_WARNING(CheatEngine,
|
LOG_WARNING(CheatEngine,
|
||||||
"Attempted to read input state, but applet resource is not initialized!");
|
"Attempted to read input state, but applet resource is not initialized!");
|
||||||
|
|
10
src/frontend_common/CMakeLists.txt
Executable file
10
src/frontend_common/CMakeLists.txt
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
add_library(frontend_common STATIC
|
||||||
|
config.cpp
|
||||||
|
config.h
|
||||||
|
)
|
||||||
|
|
||||||
|
create_target_directory_groups(frontend_common)
|
||||||
|
target_link_libraries(frontend_common PUBLIC core SimpleIni PRIVATE common Boost::headers)
|
931
src/frontend_common/config.cpp
Executable file
931
src/frontend_common/config.cpp
Executable file
|
@ -0,0 +1,931 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/settings_common.h"
|
||||||
|
#include "common/settings_enums.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
|
#include "network/network.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
|
namespace FS = Common::FS;
|
||||||
|
|
||||||
|
Config::Config(const ConfigType config_type)
|
||||||
|
: type(config_type), global{config_type == ConfigType::GlobalConfig} {}
|
||||||
|
|
||||||
|
void Config::Initialize(const std::string& config_name) {
|
||||||
|
const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
|
||||||
|
const auto config_file = fmt::format("{}.ini", config_name);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ConfigType::GlobalConfig:
|
||||||
|
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
|
||||||
|
void(FS::CreateParentDir(config_loc));
|
||||||
|
SetUpIni();
|
||||||
|
Reload();
|
||||||
|
break;
|
||||||
|
case ConfigType::PerGameConfig:
|
||||||
|
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
|
||||||
|
void(FS::CreateParentDir(config_loc));
|
||||||
|
SetUpIni();
|
||||||
|
Reload();
|
||||||
|
break;
|
||||||
|
case ConfigType::InputProfile:
|
||||||
|
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
|
||||||
|
void(FS::CreateParentDir(config_loc));
|
||||||
|
SetUpIni();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Initialize(const std::optional<std::string> config_path) {
|
||||||
|
const std::filesystem::path default_sdl_config_path =
|
||||||
|
FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
|
||||||
|
config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path));
|
||||||
|
void(FS::CreateParentDir(config_loc));
|
||||||
|
SetUpIni();
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::WriteToIni() const {
|
||||||
|
if (const SI_Error rc = config->SaveFile(config_loc.c_str()); rc < 0) {
|
||||||
|
LOG_ERROR(Frontend, "Config file could not be saved!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SetUpIni() {
|
||||||
|
config = std::make_unique<CSimpleIniA>();
|
||||||
|
config->SetUnicode(true);
|
||||||
|
config->SetSpaces(false);
|
||||||
|
config->LoadFile(config_loc.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::IsCustomConfig() const {
|
||||||
|
return type == ConfigType::PerGameConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix.append("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
const auto profile_name =
|
||||||
|
ReadStringSetting(std::string(player_prefix).append("profile_name"));
|
||||||
|
if (profile_name.empty()) {
|
||||||
|
// Use the global input config
|
||||||
|
player = Settings::values.players.GetValue(true)[player_index];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.profile_name = profile_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player_prefix.empty() && Settings::IsConfiguringGlobal()) {
|
||||||
|
const auto controller = static_cast<Settings::ControllerType>(
|
||||||
|
ReadIntegerSetting(std::string(player_prefix).append("type"),
|
||||||
|
static_cast<u8>(Settings::ControllerType::ProController)));
|
||||||
|
|
||||||
|
if (controller == Settings::ControllerType::LeftJoycon ||
|
||||||
|
controller == Settings::ControllerType::RightJoycon) {
|
||||||
|
player.controller_type = controller;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string connected_key = player_prefix;
|
||||||
|
player.connected = ReadBooleanSetting(connected_key.append("connected"),
|
||||||
|
std::make_optional(player_index == 0));
|
||||||
|
|
||||||
|
player.controller_type = static_cast<Settings::ControllerType>(
|
||||||
|
ReadIntegerSetting(std::string(player_prefix).append("type"),
|
||||||
|
static_cast<u8>(Settings::ControllerType::ProController)));
|
||||||
|
|
||||||
|
player.vibration_enabled = ReadBooleanSetting(
|
||||||
|
std::string(player_prefix).append("vibration_enabled"), std::make_optional(true));
|
||||||
|
|
||||||
|
player.vibration_strength = static_cast<int>(
|
||||||
|
ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100));
|
||||||
|
|
||||||
|
player.body_color_left = static_cast<u32>(ReadIntegerSetting(
|
||||||
|
std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE));
|
||||||
|
player.body_color_right = static_cast<u32>(ReadIntegerSetting(
|
||||||
|
std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED));
|
||||||
|
player.button_color_left = static_cast<u32>(
|
||||||
|
ReadIntegerSetting(std::string(player_prefix).append("button_color_left"),
|
||||||
|
Settings::JOYCON_BUTTONS_NEON_BLUE));
|
||||||
|
player.button_color_right = static_cast<u32>(
|
||||||
|
ReadIntegerSetting(std::string(player_prefix).append("button_color_right"),
|
||||||
|
Settings::JOYCON_BUTTONS_NEON_RED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadTouchscreenValues() {
|
||||||
|
Settings::values.touchscreen.enabled =
|
||||||
|
ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true));
|
||||||
|
Settings::values.touchscreen.rotation_angle =
|
||||||
|
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
|
||||||
|
Settings::values.touchscreen.diameter_x =
|
||||||
|
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15));
|
||||||
|
Settings::values.touchscreen.diameter_y =
|
||||||
|
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadAudioValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Audio);
|
||||||
|
ReadCategory(Settings::Category::UiAudio);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadControlValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Controls);
|
||||||
|
|
||||||
|
Settings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||||
|
ReadPlayerValues(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable docked mode if handheld is selected
|
||||||
|
const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
|
||||||
|
if (controller_type == Settings::ControllerType::Handheld) {
|
||||||
|
Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
|
||||||
|
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ReadTouchscreenValues();
|
||||||
|
ReadMotionTouchValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadMotionTouchValues() {
|
||||||
|
int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps"));
|
||||||
|
|
||||||
|
if (num_touch_from_button_maps > 0) {
|
||||||
|
for (int i = 0; i < num_touch_from_button_maps; ++i) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
|
||||||
|
Settings::TouchFromButtonMap map;
|
||||||
|
map.name = ReadStringSetting(std::string("name"), std::string("default"));
|
||||||
|
|
||||||
|
const int num_touch_maps = BeginArray(std::string("entries"));
|
||||||
|
map.buttons.reserve(num_touch_maps);
|
||||||
|
for (int j = 0; j < num_touch_maps; j++) {
|
||||||
|
SetArrayIndex(j);
|
||||||
|
std::string touch_mapping = ReadStringSetting(std::string("bind"));
|
||||||
|
map.buttons.emplace_back(std::move(touch_mapping));
|
||||||
|
}
|
||||||
|
EndArray(); // entries
|
||||||
|
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Settings::values.touch_from_button_maps.emplace_back(
|
||||||
|
Settings::TouchFromButtonMap{"default", {}});
|
||||||
|
num_touch_from_button_maps = 1;
|
||||||
|
}
|
||||||
|
EndArray(); // touch_from_button_maps
|
||||||
|
|
||||||
|
Settings::values.touch_from_button_map_index = std::clamp(
|
||||||
|
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadCoreValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Core);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadDataStorageValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
|
||||||
|
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory")));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory")));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory")));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory")));
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory")));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::DataStorage);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadDebuggingValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
|
||||||
|
|
||||||
|
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||||
|
Settings::values.record_frame_times =
|
||||||
|
ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Debugging);
|
||||||
|
ReadCategory(Settings::Category::DebuggingGraphics);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadServiceValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Services);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadDisabledAddOnValues() {
|
||||||
|
// Custom config section
|
||||||
|
BeginGroup(std::string("DisabledAddOns"));
|
||||||
|
|
||||||
|
const int size = BeginArray(std::string(""));
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
const auto title_id = ReadIntegerSetting(std::string("title_id"), 0);
|
||||||
|
std::vector<std::string> out;
|
||||||
|
const int d_size = BeginArray("disabled");
|
||||||
|
for (int j = 0; j < d_size; ++j) {
|
||||||
|
SetArrayIndex(j);
|
||||||
|
out.push_back(ReadStringSetting(std::string("d"), std::string("")));
|
||||||
|
}
|
||||||
|
EndArray(); // d
|
||||||
|
Settings::values.disabled_addons.insert_or_assign(title_id, out);
|
||||||
|
}
|
||||||
|
EndArray(); // Base disabled addons array - Has no base key
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadMiscellaneousValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Miscellaneous);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadCpuValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Cpu);
|
||||||
|
ReadCategory(Settings::Category::CpuDebug);
|
||||||
|
ReadCategory(Settings::Category::CpuUnsafe);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadRendererValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Renderer);
|
||||||
|
ReadCategory(Settings::Category::RendererAdvanced);
|
||||||
|
ReadCategory(Settings::Category::RendererDebug);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadScreenshotValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Screenshots);
|
||||||
|
FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir,
|
||||||
|
ReadStringSetting(std::string("screenshot_path"),
|
||||||
|
FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadSystemValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::System));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::System);
|
||||||
|
ReadCategory(Settings::Category::SystemAudio);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadWebServiceValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::WebService);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadNetworkValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Network);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadValues() {
|
||||||
|
if (global) {
|
||||||
|
ReadDataStorageValues();
|
||||||
|
ReadDebuggingValues();
|
||||||
|
ReadDisabledAddOnValues();
|
||||||
|
ReadNetworkValues();
|
||||||
|
ReadServiceValues();
|
||||||
|
ReadWebServiceValues();
|
||||||
|
ReadMiscellaneousValues();
|
||||||
|
}
|
||||||
|
ReadControlValues();
|
||||||
|
ReadCoreValues();
|
||||||
|
ReadCpuValues();
|
||||||
|
ReadRendererValues();
|
||||||
|
ReadAudioValues();
|
||||||
|
ReadSystemValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SavePlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix = std::string("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
if (player.profile_name.empty()) {
|
||||||
|
// No custom profile selected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
|
||||||
|
std::make_optional(std::string("")));
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
|
||||||
|
std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
|
||||||
|
|
||||||
|
if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
|
||||||
|
WriteSetting(std::string(player_prefix).append("connected"), player.connected,
|
||||||
|
std::make_optional(player_index == 0));
|
||||||
|
WriteSetting(std::string(player_prefix).append("vibration_enabled"),
|
||||||
|
player.vibration_enabled, std::make_optional(true));
|
||||||
|
WriteSetting(std::string(player_prefix).append("vibration_strength"),
|
||||||
|
player.vibration_strength, std::make_optional(100));
|
||||||
|
WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left,
|
||||||
|
std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
|
||||||
|
WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right,
|
||||||
|
std::make_optional(Settings::JOYCON_BODY_NEON_RED));
|
||||||
|
WriteSetting(std::string(player_prefix).append("button_color_left"),
|
||||||
|
player.button_color_left,
|
||||||
|
std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
|
||||||
|
WriteSetting(std::string(player_prefix).append("button_color_right"),
|
||||||
|
player.button_color_right,
|
||||||
|
std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveTouchscreenValues() {
|
||||||
|
const auto& touchscreen = Settings::values.touchscreen;
|
||||||
|
|
||||||
|
WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true));
|
||||||
|
|
||||||
|
WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
|
||||||
|
std::make_optional(static_cast<u32>(0)));
|
||||||
|
WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
|
||||||
|
std::make_optional(static_cast<u32>(15)));
|
||||||
|
WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
|
||||||
|
std::make_optional(static_cast<u32>(15)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveMotionTouchValues() {
|
||||||
|
BeginArray(std::string("touch_from_button_maps"));
|
||||||
|
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
|
||||||
|
SetArrayIndex(static_cast<int>(p));
|
||||||
|
WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
|
||||||
|
std::make_optional(std::string("default")));
|
||||||
|
|
||||||
|
BeginArray(std::string("entries"));
|
||||||
|
for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
|
||||||
|
++q) {
|
||||||
|
SetArrayIndex(static_cast<int>(q));
|
||||||
|
WriteSetting(std::string("bind"),
|
||||||
|
Settings::values.touch_from_button_maps[p].buttons[q]);
|
||||||
|
}
|
||||||
|
EndArray(); // entries
|
||||||
|
}
|
||||||
|
EndArray(); // touch_from_button_maps
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveValues() {
|
||||||
|
if (global) {
|
||||||
|
SaveDataStorageValues();
|
||||||
|
SaveDebuggingValues();
|
||||||
|
SaveDisabledAddOnValues();
|
||||||
|
SaveNetworkValues();
|
||||||
|
SaveWebServiceValues();
|
||||||
|
SaveMiscellaneousValues();
|
||||||
|
}
|
||||||
|
SaveControlValues();
|
||||||
|
SaveCoreValues();
|
||||||
|
SaveCpuValues();
|
||||||
|
SaveRendererValues();
|
||||||
|
SaveAudioValues();
|
||||||
|
SaveSystemValues();
|
||||||
|
|
||||||
|
WriteToIni();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveAudioValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Audio);
|
||||||
|
WriteCategory(Settings::Category::UiAudio);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveControlValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Controls);
|
||||||
|
|
||||||
|
Settings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||||
|
SavePlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveTouchscreenValues();
|
||||||
|
SaveMotionTouchValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveCoreValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Core);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveDataStorageValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
|
||||||
|
|
||||||
|
WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
|
||||||
|
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
|
||||||
|
WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
|
||||||
|
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
|
||||||
|
WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
|
||||||
|
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
|
||||||
|
WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
|
||||||
|
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||||
|
WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
|
||||||
|
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::DataStorage);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveDebuggingValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
|
||||||
|
|
||||||
|
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||||
|
WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Debugging);
|
||||||
|
WriteCategory(Settings::Category::DebuggingGraphics);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveNetworkValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Network);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveDisabledAddOnValues() {
|
||||||
|
// Custom config section
|
||||||
|
BeginGroup(std::string("DisabledAddOns"));
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
BeginArray(std::string(""));
|
||||||
|
for (const auto& elem : Settings::values.disabled_addons) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0)));
|
||||||
|
BeginArray(std::string("disabled"));
|
||||||
|
for (std::size_t j = 0; j < elem.second.size(); ++j) {
|
||||||
|
SetArrayIndex(static_cast<int>(j));
|
||||||
|
WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
|
||||||
|
}
|
||||||
|
EndArray(); // disabled
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
EndArray(); // Base disabled addons array - Has no base key
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveMiscellaneousValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Miscellaneous);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveCpuValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Cpu);
|
||||||
|
WriteCategory(Settings::Category::CpuDebug);
|
||||||
|
WriteCategory(Settings::Category::CpuUnsafe);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveRendererValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Renderer);
|
||||||
|
WriteCategory(Settings::Category::RendererAdvanced);
|
||||||
|
WriteCategory(Settings::Category::RendererDebug);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveScreenshotValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
|
||||||
|
|
||||||
|
WriteSetting(std::string("screenshot_path"),
|
||||||
|
FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
|
||||||
|
WriteCategory(Settings::Category::Screenshots);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveSystemValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::System));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::System);
|
||||||
|
WriteCategory(Settings::Category::SystemAudio);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SaveWebServiceValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::WebService);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
|
||||||
|
std::string full_key = GetFullKey(key, false);
|
||||||
|
if (!default_value.has_value()) {
|
||||||
|
return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->GetBoolValue(GetSection().c_str(),
|
||||||
|
std::string(full_key).append("\\default").c_str(), false)) {
|
||||||
|
return static_cast<bool>(default_value.value());
|
||||||
|
} else {
|
||||||
|
return config->GetBoolValue(GetSection().c_str(), full_key.c_str(),
|
||||||
|
static_cast<bool>(default_value.value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) {
|
||||||
|
std::string full_key = GetFullKey(key, false);
|
||||||
|
if (!default_value.has_value()) {
|
||||||
|
try {
|
||||||
|
return std::stoll(
|
||||||
|
std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
|
||||||
|
} catch (...) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 result = 0;
|
||||||
|
if (config->GetBoolValue(GetSection().c_str(),
|
||||||
|
std::string(full_key).append("\\default").c_str(), true)) {
|
||||||
|
result = default_value.value();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
result = std::stoll(std::string(config->GetValue(
|
||||||
|
GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
|
||||||
|
} catch (...) {
|
||||||
|
result = default_value.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Config::ReadDoubleSetting(const std::string& key,
|
||||||
|
const std::optional<double> default_value) {
|
||||||
|
std::string full_key = GetFullKey(key, false);
|
||||||
|
if (!default_value.has_value()) {
|
||||||
|
return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
double result;
|
||||||
|
if (config->GetBoolValue(GetSection().c_str(),
|
||||||
|
std::string(full_key).append("\\default").c_str(), true)) {
|
||||||
|
result = default_value.value();
|
||||||
|
} else {
|
||||||
|
result =
|
||||||
|
config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::ReadStringSetting(const std::string& key,
|
||||||
|
const std::optional<std::string> default_value) {
|
||||||
|
std::string result;
|
||||||
|
std::string full_key = GetFullKey(key, false);
|
||||||
|
if (!default_value.has_value()) {
|
||||||
|
result = config->GetValue(GetSection().c_str(), full_key.c_str(), "");
|
||||||
|
boost::replace_all(result, "\"", "");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->GetBoolValue(GetSection().c_str(),
|
||||||
|
std::string(full_key).append("\\default").c_str(), true)) {
|
||||||
|
result = default_value.value();
|
||||||
|
} else {
|
||||||
|
result =
|
||||||
|
config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str());
|
||||||
|
}
|
||||||
|
boost::replace_all(result, "\"", "");
|
||||||
|
boost::replace_all(result, "//", "/");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::Exists(const std::string& section, const std::string& key) const {
|
||||||
|
const std::string value = config->GetValue(section.c_str(), key.c_str(), "");
|
||||||
|
return !value.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
void Config::WriteSetting(const std::string& key, const Type& value,
|
||||||
|
const std::optional<Type>& default_value,
|
||||||
|
const std::optional<bool>& use_global) {
|
||||||
|
std::string full_key = GetFullKey(key, false);
|
||||||
|
|
||||||
|
std::string saved_value;
|
||||||
|
std::string string_default;
|
||||||
|
if constexpr (std::is_same_v<Type, std::string>) {
|
||||||
|
saved_value.append(AdjustOutputString(value));
|
||||||
|
if (default_value.has_value()) {
|
||||||
|
string_default.append(AdjustOutputString(default_value.value()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
saved_value.append(AdjustOutputString(ToString(value)));
|
||||||
|
if (default_value.has_value()) {
|
||||||
|
string_default.append(ToString(default_value.value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default_value.has_value() && use_global.has_value()) {
|
||||||
|
if (!global) {
|
||||||
|
WriteSettingInternal(std::string(full_key).append("\\global"),
|
||||||
|
ToString(use_global.value()));
|
||||||
|
}
|
||||||
|
if (global || use_global.value() == false) {
|
||||||
|
WriteSettingInternal(std::string(full_key).append("\\default"),
|
||||||
|
ToString(string_default == saved_value));
|
||||||
|
WriteSettingInternal(full_key, saved_value);
|
||||||
|
}
|
||||||
|
} else if (default_value.has_value() && !use_global.has_value()) {
|
||||||
|
WriteSettingInternal(std::string(full_key).append("\\default"),
|
||||||
|
ToString(string_default == saved_value));
|
||||||
|
WriteSettingInternal(full_key, saved_value);
|
||||||
|
} else {
|
||||||
|
WriteSettingInternal(full_key, saved_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::WriteSettingInternal(const std::string& key, const std::string& value) {
|
||||||
|
config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Reload() {
|
||||||
|
ReadValues();
|
||||||
|
// To apply default value changes
|
||||||
|
SaveValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Save() {
|
||||||
|
SaveValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ClearControlPlayerValues() const {
|
||||||
|
// If key is an empty string, all keys in the current group() are removed.
|
||||||
|
const char* section = Settings::TranslateCategory(Settings::Category::Controls);
|
||||||
|
CSimpleIniA::TNamesDepend keys;
|
||||||
|
config->GetAllKeys(section, keys);
|
||||||
|
for (const auto& key : keys) {
|
||||||
|
if (std::string(config->GetValue(section, key.pItem)).empty()) {
|
||||||
|
config->Delete(section, key.pItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Config::GetConfigFilePath() const {
|
||||||
|
return config_loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadCategory(const Settings::Category category) {
|
||||||
|
const auto& settings = FindRelevantList(category);
|
||||||
|
std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::WriteCategory(const Settings::Category category) {
|
||||||
|
const auto& settings = FindRelevantList(category);
|
||||||
|
std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
|
||||||
|
if (!setting->Save() || (!setting->Switchable() && !global)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string key = AdjustKey(setting->GetLabel());
|
||||||
|
const std::string default_value(setting->DefaultToString());
|
||||||
|
|
||||||
|
bool use_global = true;
|
||||||
|
if (setting->Switchable() && !global) {
|
||||||
|
use_global =
|
||||||
|
ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true));
|
||||||
|
setting->SetGlobal(use_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global || !use_global) {
|
||||||
|
const bool is_default =
|
||||||
|
ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true));
|
||||||
|
if (!is_default) {
|
||||||
|
const std::string setting_string = ReadStringSetting(key, default_value);
|
||||||
|
setting->LoadString(setting_string);
|
||||||
|
} else {
|
||||||
|
// Empty string resets the Setting to default
|
||||||
|
setting->LoadString("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
|
||||||
|
if (!setting->Save()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = AdjustKey(setting->GetLabel());
|
||||||
|
if (setting->Switchable()) {
|
||||||
|
if (!global) {
|
||||||
|
WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
|
||||||
|
}
|
||||||
|
if (global || !setting->UsingGlobal()) {
|
||||||
|
WriteSetting(std::string(key).append("\\default"),
|
||||||
|
setting->ToString() == setting->DefaultToString());
|
||||||
|
WriteSetting(key, setting->ToString());
|
||||||
|
}
|
||||||
|
} else if (global) {
|
||||||
|
WriteSetting(std::string(key).append("\\default"),
|
||||||
|
setting->ToString() == setting->DefaultToString());
|
||||||
|
WriteSetting(key, setting->ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::BeginGroup(const std::string& group) {
|
||||||
|
// You can't begin a group while reading/writing from a config array
|
||||||
|
ASSERT(array_stack.empty());
|
||||||
|
|
||||||
|
key_stack.push_back(AdjustKey(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::EndGroup() {
|
||||||
|
// You can't end a group if you haven't started one yet
|
||||||
|
ASSERT(!key_stack.empty());
|
||||||
|
|
||||||
|
// You can't end a group when reading/writing from a config array
|
||||||
|
ASSERT(array_stack.empty());
|
||||||
|
|
||||||
|
key_stack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::GetSection() {
|
||||||
|
if (key_stack.empty()) {
|
||||||
|
return std::string{""};
|
||||||
|
}
|
||||||
|
|
||||||
|
return key_stack.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::GetGroup() const {
|
||||||
|
if (key_stack.size() <= 1) {
|
||||||
|
return std::string{""};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key;
|
||||||
|
for (size_t i = 1; i < key_stack.size(); ++i) {
|
||||||
|
key.append(key_stack[i]).append("\\");
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::AdjustKey(const std::string& key) {
|
||||||
|
std::string adjusted_key(key);
|
||||||
|
boost::replace_all(adjusted_key, "/", "\\");
|
||||||
|
boost::replace_all(adjusted_key, " ", "%20");
|
||||||
|
return adjusted_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::AdjustOutputString(const std::string& string) {
|
||||||
|
std::string adjusted_string(string);
|
||||||
|
boost::replace_all(adjusted_string, "\\", "/");
|
||||||
|
boost::replace_all(adjusted_string, "//", "/");
|
||||||
|
|
||||||
|
// Needed for backwards compatibility with QSettings deserialization
|
||||||
|
for (const auto& special_character : special_characters) {
|
||||||
|
if (adjusted_string.find(special_character) != std::string::npos) {
|
||||||
|
adjusted_string.insert(0, "\"");
|
||||||
|
adjusted_string.append("\"");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return adjusted_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) {
|
||||||
|
if (array_stack.empty()) {
|
||||||
|
return std::string(GetGroup()).append(AdjustKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string array_key;
|
||||||
|
for (size_t i = 0; i < array_stack.size(); ++i) {
|
||||||
|
if (!array_stack[i].name.empty()) {
|
||||||
|
array_key.append(array_stack[i].name).append("\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) {
|
||||||
|
array_key.append(ToString(array_stack[i].index)).append("\\");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key));
|
||||||
|
return final_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Config::BeginArray(const std::string& array) {
|
||||||
|
array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0});
|
||||||
|
const int size = config->GetLongValue(GetSection().c_str(),
|
||||||
|
GetFullKey(std::string("size"), true).c_str(), 0);
|
||||||
|
array_stack.back().size = size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::EndArray() {
|
||||||
|
// You can't end a config array before starting one
|
||||||
|
ASSERT(!array_stack.empty());
|
||||||
|
|
||||||
|
// Write out the size to config
|
||||||
|
if (key_stack.size() == 1 && array_stack.back().name.empty()) {
|
||||||
|
// Edge-case where the first array created doesn't have a name
|
||||||
|
config->SetValue(GetSection().c_str(), std::string("size").c_str(),
|
||||||
|
ToString(array_stack.back().size).c_str());
|
||||||
|
} else {
|
||||||
|
const auto key = GetFullKey(std::string("size"), true);
|
||||||
|
config->SetValue(GetSection().c_str(), key.c_str(),
|
||||||
|
ToString(array_stack.back().size).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
array_stack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SetArrayIndex(const int index) {
|
||||||
|
// You can't set the array index if you haven't started one yet
|
||||||
|
ASSERT(!array_stack.empty());
|
||||||
|
|
||||||
|
const int array_index = index + 1;
|
||||||
|
|
||||||
|
// You can't exceed the known max size of the array by more than 1
|
||||||
|
ASSERT(array_stack.front().size + 1 >= array_index);
|
||||||
|
|
||||||
|
// Change the config array size to the current index since you may want
|
||||||
|
// to reduce the number of elements that you read back from the config
|
||||||
|
// in the future.
|
||||||
|
array_stack.back().size = array_index;
|
||||||
|
array_stack.back().index = array_index;
|
||||||
|
}
|
206
src/frontend_common/config.h
Executable file
206
src/frontend_common/config.h
Executable file
|
@ -0,0 +1,206 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include "common/settings.h"
|
||||||
|
|
||||||
|
#include <SimpleIni.h>
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
|
// Workaround for conflicting definition in libloaderapi.h caused by SimpleIni
|
||||||
|
#undef LoadString
|
||||||
|
#undef CreateFile
|
||||||
|
#undef DeleteFile
|
||||||
|
#undef CopyFile
|
||||||
|
#undef CreateDirectory
|
||||||
|
#undef MoveFile
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
public:
|
||||||
|
enum class ConfigType {
|
||||||
|
GlobalConfig,
|
||||||
|
PerGameConfig,
|
||||||
|
InputProfile,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~Config() = default;
|
||||||
|
|
||||||
|
void ClearControlPlayerValues() const;
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string& GetConfigFilePath() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool Exists(const std::string& section, const std::string& key) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit Config(ConfigType config_type = ConfigType::GlobalConfig);
|
||||||
|
|
||||||
|
void Initialize(const std::string& config_name = "config");
|
||||||
|
void Initialize(std::optional<std::string> config_path);
|
||||||
|
|
||||||
|
void WriteToIni() const;
|
||||||
|
|
||||||
|
void SetUpIni();
|
||||||
|
[[nodiscard]] bool IsCustomConfig() const;
|
||||||
|
|
||||||
|
void Reload();
|
||||||
|
void Save();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived config classes must implement this so they can reload all platform-specific
|
||||||
|
* values and global ones.
|
||||||
|
*/
|
||||||
|
virtual void ReloadAllValues() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived config classes must implement this so they can save all platform-specific
|
||||||
|
* and global values.
|
||||||
|
*/
|
||||||
|
virtual void SaveAllValues() = 0;
|
||||||
|
|
||||||
|
void ReadValues();
|
||||||
|
void ReadPlayerValues(std::size_t player_index);
|
||||||
|
|
||||||
|
void ReadTouchscreenValues();
|
||||||
|
void ReadMotionTouchValues();
|
||||||
|
|
||||||
|
// Read functions bases off the respective config section names.
|
||||||
|
void ReadAudioValues();
|
||||||
|
void ReadControlValues();
|
||||||
|
void ReadCoreValues();
|
||||||
|
void ReadDataStorageValues();
|
||||||
|
void ReadDebuggingValues();
|
||||||
|
void ReadServiceValues();
|
||||||
|
void ReadDisabledAddOnValues();
|
||||||
|
void ReadMiscellaneousValues();
|
||||||
|
void ReadCpuValues();
|
||||||
|
void ReadRendererValues();
|
||||||
|
void ReadScreenshotValues();
|
||||||
|
void ReadSystemValues();
|
||||||
|
void ReadWebServiceValues();
|
||||||
|
void ReadNetworkValues();
|
||||||
|
|
||||||
|
// Read platform specific sections
|
||||||
|
virtual void ReadHidbusValues() = 0;
|
||||||
|
virtual void ReadDebugControlValues() = 0;
|
||||||
|
virtual void ReadPathValues() = 0;
|
||||||
|
virtual void ReadShortcutValues() = 0;
|
||||||
|
virtual void ReadUIValues() = 0;
|
||||||
|
virtual void ReadUIGamelistValues() = 0;
|
||||||
|
virtual void ReadUILayoutValues() = 0;
|
||||||
|
virtual void ReadMultiplayerValues() = 0;
|
||||||
|
|
||||||
|
void SaveValues();
|
||||||
|
void SavePlayerValues(std::size_t player_index);
|
||||||
|
void SaveTouchscreenValues();
|
||||||
|
void SaveMotionTouchValues();
|
||||||
|
|
||||||
|
// Save functions based off the respective config section names.
|
||||||
|
void SaveAudioValues();
|
||||||
|
void SaveControlValues();
|
||||||
|
void SaveCoreValues();
|
||||||
|
void SaveDataStorageValues();
|
||||||
|
void SaveDebuggingValues();
|
||||||
|
void SaveNetworkValues();
|
||||||
|
void SaveDisabledAddOnValues();
|
||||||
|
void SaveMiscellaneousValues();
|
||||||
|
void SaveCpuValues();
|
||||||
|
void SaveRendererValues();
|
||||||
|
void SaveScreenshotValues();
|
||||||
|
void SaveSystemValues();
|
||||||
|
void SaveWebServiceValues();
|
||||||
|
|
||||||
|
// Save platform specific sections
|
||||||
|
virtual void SaveHidbusValues() = 0;
|
||||||
|
virtual void SaveDebugControlValues() = 0;
|
||||||
|
virtual void SavePathValues() = 0;
|
||||||
|
virtual void SaveShortcutValues() = 0;
|
||||||
|
virtual void SaveUIValues() = 0;
|
||||||
|
virtual void SaveUIGamelistValues() = 0;
|
||||||
|
virtual void SaveUILayoutValues() = 0;
|
||||||
|
virtual void SaveMultiplayerValues() = 0;
|
||||||
|
|
||||||
|
virtual std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a setting from the qt_config.
|
||||||
|
*
|
||||||
|
* @param key The setting's identifier
|
||||||
|
* @param default_value The value to use when the setting is not already present in the config
|
||||||
|
*/
|
||||||
|
bool ReadBooleanSetting(const std::string& key,
|
||||||
|
std::optional<bool> default_value = std::nullopt);
|
||||||
|
s64 ReadIntegerSetting(const std::string& key, std::optional<s64> default_value = std::nullopt);
|
||||||
|
double ReadDoubleSetting(const std::string& key,
|
||||||
|
std::optional<double> default_value = std::nullopt);
|
||||||
|
std::string ReadStringSetting(const std::string& key,
|
||||||
|
std::optional<std::string> default_value = std::nullopt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a setting to the qt_config.
|
||||||
|
*
|
||||||
|
* @param key The setting's idetentifier
|
||||||
|
* @param value Value of the setting
|
||||||
|
* @param default_value Default of the setting if not present in config
|
||||||
|
* @param use_global Specifies if the custom or global config should be in use, for custom
|
||||||
|
* configs
|
||||||
|
*/
|
||||||
|
template <typename Type = int>
|
||||||
|
void WriteSetting(const std::string& key, const Type& value,
|
||||||
|
const std::optional<Type>& default_value = std::nullopt,
|
||||||
|
const std::optional<bool>& use_global = std::nullopt);
|
||||||
|
void WriteSettingInternal(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
|
void ReadCategory(Settings::Category category);
|
||||||
|
void WriteCategory(Settings::Category category);
|
||||||
|
void ReadSettingGeneric(Settings::BasicSetting* setting);
|
||||||
|
void WriteSettingGeneric(const Settings::BasicSetting* setting);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] std::string ToString(const T& value_) {
|
||||||
|
if constexpr (std::is_same_v<T, std::string>) {
|
||||||
|
return value_;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::optional<u32>>) {
|
||||||
|
return value_.has_value() ? std::to_string(*value_) : "none";
|
||||||
|
} else if constexpr (std::is_same_v<T, bool>) {
|
||||||
|
return value_ ? "true" : "false";
|
||||||
|
} else {
|
||||||
|
return std::to_string(static_cast<s64>(value_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeginGroup(const std::string& group);
|
||||||
|
void EndGroup();
|
||||||
|
std::string GetSection();
|
||||||
|
[[nodiscard]] std::string GetGroup() const;
|
||||||
|
static std::string AdjustKey(const std::string& key);
|
||||||
|
static std::string AdjustOutputString(const std::string& string);
|
||||||
|
std::string GetFullKey(const std::string& key, bool skipArrayIndex);
|
||||||
|
int BeginArray(const std::string& array);
|
||||||
|
void EndArray();
|
||||||
|
void SetArrayIndex(int index);
|
||||||
|
|
||||||
|
const ConfigType type;
|
||||||
|
std::unique_ptr<CSimpleIniA> config;
|
||||||
|
std::string config_loc;
|
||||||
|
const bool global;
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*',
|
||||||
|
'|', ';', '\'', '\"', ',', '<', '.',
|
||||||
|
'>', '?', '`', '~', '='};
|
||||||
|
|
||||||
|
struct ConfigArray {
|
||||||
|
std::string name;
|
||||||
|
int size;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
std::vector<ConfigArray> array_stack;
|
||||||
|
std::vector<std::string> key_stack;
|
||||||
|
};
|
|
@ -4,7 +4,7 @@
|
||||||
add_subdirectory(host_shaders)
|
add_subdirectory(host_shaders)
|
||||||
|
|
||||||
if(LIBVA_FOUND)
|
if(LIBVA_FOUND)
|
||||||
set_source_files_properties(host1x/codecs/codec.cpp
|
set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp
|
||||||
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
|
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
|
||||||
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
|
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
@ -67,6 +67,8 @@ add_library(video_core STATIC
|
||||||
host1x/codecs/vp9.cpp
|
host1x/codecs/vp9.cpp
|
||||||
host1x/codecs/vp9.h
|
host1x/codecs/vp9.h
|
||||||
host1x/codecs/vp9_types.h
|
host1x/codecs/vp9_types.h
|
||||||
|
host1x/ffmpeg/ffmpeg.cpp
|
||||||
|
host1x/ffmpeg/ffmpeg.h
|
||||||
host1x/control.cpp
|
host1x/control.cpp
|
||||||
host1x/control.h
|
host1x/control.h
|
||||||
host1x/host1x.cpp
|
host1x/host1x.cpp
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <vector>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/scope_exit.h"
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "video_core/host1x/codecs/codec.h"
|
#include "video_core/host1x/codecs/codec.h"
|
||||||
#include "video_core/host1x/codecs/h264.h"
|
#include "video_core/host1x/codecs/h264.h"
|
||||||
|
@ -14,242 +10,17 @@
|
||||||
#include "video_core/host1x/host1x.h"
|
#include "video_core/host1x/host1x.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <libavfilter/buffersink.h>
|
|
||||||
#include <libavfilter/buffersrc.h>
|
|
||||||
#include <libavutil/opt.h>
|
|
||||||
#ifdef LIBVA_FOUND
|
|
||||||
// for querying VAAPI driver information
|
|
||||||
#include <libavutil/hwcontext_vaapi.h>
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
namespace {
|
|
||||||
constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
|
|
||||||
constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
|
|
||||||
constexpr std::array PREFERRED_GPU_DECODERS = {
|
|
||||||
AV_HWDEVICE_TYPE_CUDA,
|
|
||||||
#ifdef _WIN32
|
|
||||||
AV_HWDEVICE_TYPE_D3D11VA,
|
|
||||||
AV_HWDEVICE_TYPE_DXVA2,
|
|
||||||
#elif defined(__unix__)
|
|
||||||
AV_HWDEVICE_TYPE_VAAPI,
|
|
||||||
AV_HWDEVICE_TYPE_VDPAU,
|
|
||||||
#endif
|
|
||||||
// last resort for Linux Flatpak (w/ NVIDIA)
|
|
||||||
AV_HWDEVICE_TYPE_VULKAN,
|
|
||||||
};
|
|
||||||
|
|
||||||
void AVPacketDeleter(AVPacket* ptr) {
|
|
||||||
av_packet_free(&ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
|
|
||||||
|
|
||||||
AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
|
|
||||||
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
|
|
||||||
if (*p == av_codec_ctx->pix_fmt) {
|
|
||||||
return av_codec_ctx->pix_fmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
|
|
||||||
av_buffer_unref(&av_codec_ctx->hw_device_ctx);
|
|
||||||
av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
|
|
||||||
return PREFERRED_CPU_FMT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all the currently available hwcontext in ffmpeg
|
|
||||||
std::vector<AVHWDeviceType> ListSupportedContexts() {
|
|
||||||
std::vector<AVHWDeviceType> contexts{};
|
|
||||||
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
|
|
||||||
do {
|
|
||||||
current_device_type = av_hwdevice_iterate_types(current_device_type);
|
|
||||||
contexts.push_back(current_device_type);
|
|
||||||
} while (current_device_type != AV_HWDEVICE_TYPE_NONE);
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void AVFrameDeleter(AVFrame* ptr) {
|
|
||||||
av_frame_free(&ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
|
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
|
||||||
: host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
|
: host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
|
||||||
vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
|
vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
|
||||||
vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
|
vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
|
||||||
|
|
||||||
Codec::~Codec() {
|
Codec::~Codec() = default;
|
||||||
if (!initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Free libav memory
|
|
||||||
avcodec_free_context(&av_codec_ctx);
|
|
||||||
av_buffer_unref(&av_gpu_decoder);
|
|
||||||
|
|
||||||
if (filters_initialized) {
|
|
||||||
avfilter_graph_free(&av_filter_graph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Codec::CreateGpuAvDevice() {
|
|
||||||
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
|
|
||||||
static const auto supported_contexts = ListSupportedContexts();
|
|
||||||
for (const auto& type : PREFERRED_GPU_DECODERS) {
|
|
||||||
if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
|
|
||||||
[&type](const auto& context) { return context == type; })) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
|
|
||||||
av_buffer_unref(&av_gpu_decoder);
|
|
||||||
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
|
|
||||||
if (hwdevice_res < 0) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
|
|
||||||
av_hwdevice_get_type_name(type), hwdevice_res);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#ifdef LIBVA_FOUND
|
|
||||||
if (type == AV_HWDEVICE_TYPE_VAAPI) {
|
|
||||||
// we need to determine if this is an impersonated VAAPI driver
|
|
||||||
AVHWDeviceContext* hwctx =
|
|
||||||
static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
|
|
||||||
AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
|
|
||||||
const char* vendor_name = vaQueryVendorString(vactx->display);
|
|
||||||
if (strstr(vendor_name, "VDPAU backend")) {
|
|
||||||
// VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
|
|
||||||
LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
// according to some user testing, certain vaapi driver (Intel?) could be buggy
|
|
||||||
// so let's log the driver name which may help the developers/supporters
|
|
||||||
LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
|
|
||||||
if (!config) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
|
|
||||||
av_codec->name, av_hwdevice_get_type_name(type));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
|
|
||||||
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
|
||||||
av_codec_ctx->pix_fmt = config->pix_fmt;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Codec::InitializeAvCodecContext() {
|
|
||||||
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
|
||||||
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
|
||||||
av_codec_ctx->thread_count = 0;
|
|
||||||
av_codec_ctx->thread_type &= ~FF_THREAD_FRAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Codec::InitializeGpuDecoder() {
|
|
||||||
if (!CreateGpuAvDevice()) {
|
|
||||||
av_buffer_unref(&av_gpu_decoder);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
|
|
||||||
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
|
||||||
av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
|
||||||
av_codec_ctx->get_format = GetGpuFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Codec::InitializeAvFilters(AVFrame* frame) {
|
|
||||||
const AVFilter* buffer_src = avfilter_get_by_name("buffer");
|
|
||||||
const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
|
|
||||||
AVFilterInOut* inputs = avfilter_inout_alloc();
|
|
||||||
AVFilterInOut* outputs = avfilter_inout_alloc();
|
|
||||||
SCOPE_EXIT({
|
|
||||||
avfilter_inout_free(&inputs);
|
|
||||||
avfilter_inout_free(&outputs);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Don't know how to get the accurate time_base but it doesn't matter for yadif filter
|
|
||||||
// so just use 1/1 to make buffer filter happy
|
|
||||||
std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width,
|
|
||||||
frame->height, frame->format);
|
|
||||||
|
|
||||||
av_filter_graph = avfilter_graph_alloc();
|
|
||||||
int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(),
|
|
||||||
nullptr, av_filter_graph);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr,
|
|
||||||
av_filter_graph);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs->name = av_strdup("out");
|
|
||||||
inputs->filter_ctx = av_filter_sink_ctx;
|
|
||||||
inputs->pad_idx = 0;
|
|
||||||
inputs->next = nullptr;
|
|
||||||
|
|
||||||
outputs->name = av_strdup("in");
|
|
||||||
outputs->filter_ctx = av_filter_src_ctx;
|
|
||||||
outputs->pad_idx = 0;
|
|
||||||
outputs->next = nullptr;
|
|
||||||
|
|
||||||
const char* description = "yadif=1:-1:0";
|
|
||||||
ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = avfilter_graph_config(av_filter_graph, nullptr);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
filters_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Codec::Initialize() {
|
void Codec::Initialize() {
|
||||||
const AVCodecID codec = [&] {
|
initialized = decode_api.Initialize(current_codec);
|
||||||
switch (current_codec) {
|
|
||||||
case Host1x::NvdecCommon::VideoCodec::H264:
|
|
||||||
return AV_CODEC_ID_H264;
|
|
||||||
case Host1x::NvdecCommon::VideoCodec::VP8:
|
|
||||||
return AV_CODEC_ID_VP8;
|
|
||||||
case Host1x::NvdecCommon::VideoCodec::VP9:
|
|
||||||
return AV_CODEC_ID_VP9;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
|
|
||||||
return AV_CODEC_ID_NONE;
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
av_codec = avcodec_find_decoder(codec);
|
|
||||||
|
|
||||||
InitializeAvCodecContext();
|
|
||||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
|
||||||
InitializeGpuDecoder();
|
|
||||||
}
|
|
||||||
if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
|
|
||||||
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
|
|
||||||
avcodec_free_context(&av_codec_ctx);
|
|
||||||
av_buffer_unref(&av_gpu_decoder);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!av_codec_ctx->hw_device_ctx) {
|
|
||||||
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
|
|
||||||
}
|
|
||||||
initialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
|
void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
|
||||||
|
@ -264,14 +35,18 @@ void Codec::Decode() {
|
||||||
if (is_first_frame) {
|
if (is_first_frame) {
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assemble bitstream.
|
||||||
bool vp9_hidden_frame = false;
|
bool vp9_hidden_frame = false;
|
||||||
const auto& frame_data = [&]() {
|
size_t configuration_size = 0;
|
||||||
|
const auto packet_data = [&]() {
|
||||||
switch (current_codec) {
|
switch (current_codec) {
|
||||||
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
|
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
|
||||||
return h264_decoder->ComposeFrame(state, is_first_frame);
|
return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
|
||||||
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
|
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
|
||||||
return vp8_decoder->ComposeFrame(state);
|
return vp8_decoder->ComposeFrame(state);
|
||||||
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
|
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
|
||||||
|
@ -283,89 +58,35 @@ void Codec::Decode() {
|
||||||
return std::span<const u8>{};
|
return std::span<const u8>{};
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter};
|
|
||||||
if (!packet) {
|
// Send assembled bitstream to decoder.
|
||||||
LOG_ERROR(Service_NVDRV, "av_packet_alloc failed");
|
if (!decode_api.SendPacket(packet_data, configuration_size)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
packet->data = const_cast<u8*>(frame_data.data());
|
|
||||||
packet->size = static_cast<s32>(frame_data.size());
|
// Only receive/store visible frames.
|
||||||
if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Only receive/store visible frames
|
|
||||||
if (vp9_hidden_frame) {
|
if (vp9_hidden_frame) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
|
|
||||||
AVFramePtr final_frame{nullptr, AVFrameDeleter};
|
|
||||||
ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
|
|
||||||
if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (initial_frame->width == 0 || initial_frame->height == 0) {
|
|
||||||
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool is_interlaced = initial_frame->interlaced_frame != 0;
|
|
||||||
if (av_codec_ctx->hw_device_ctx) {
|
|
||||||
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
|
||||||
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
|
||||||
// Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
|
|
||||||
// because Intel drivers crash unless using AV_PIX_FMT_NV12
|
|
||||||
final_frame->format = PREFERRED_GPU_FMT;
|
|
||||||
const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
|
|
||||||
ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
|
|
||||||
} else {
|
|
||||||
final_frame = std::move(initial_frame);
|
|
||||||
}
|
|
||||||
if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
|
|
||||||
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!is_interlaced) {
|
|
||||||
av_frames.push(std::move(final_frame));
|
|
||||||
} else {
|
|
||||||
if (!filters_initialized) {
|
|
||||||
InitializeAvFilters(final_frame.get());
|
|
||||||
}
|
|
||||||
if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(),
|
|
||||||
AV_BUFFERSRC_FLAG_KEEP_REF);
|
|
||||||
ret) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
|
||||||
|
|
||||||
int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get());
|
// Receive output frames from decoder.
|
||||||
|
decode_api.ReceiveFrames(frames);
|
||||||
|
|
||||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF))
|
while (frames.size() > 10) {
|
||||||
break;
|
LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
|
||||||
if (ret < 0) {
|
frames.pop();
|
||||||
LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
av_frames.push(std::move(filter_frame));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (av_frames.size() > 10) {
|
|
||||||
LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
|
|
||||||
av_frames.pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFramePtr Codec::GetCurrentFrame() {
|
std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
|
||||||
// Sometimes VIC will request more frames than have been decoded.
|
// Sometimes VIC will request more frames than have been decoded.
|
||||||
// in this case, return a nullptr and don't overwrite previous frame data
|
// in this case, return a blank frame and don't overwrite previous data.
|
||||||
if (av_frames.empty()) {
|
if (frames.empty()) {
|
||||||
return AVFramePtr{nullptr, AVFrameDeleter};
|
return {};
|
||||||
}
|
}
|
||||||
AVFramePtr frame = std::move(av_frames.front());
|
|
||||||
av_frames.pop();
|
auto frame = std::move(frames.front());
|
||||||
|
frames.pop();
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,28 +4,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/host1x/ffmpeg/ffmpeg.h"
|
||||||
#include "video_core/host1x/nvdec_common.h"
|
#include "video_core/host1x/nvdec_common.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wconversion"
|
|
||||||
#endif
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
#include <libavfilter/avfilter.h>
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
void AVFrameDeleter(AVFrame* ptr);
|
|
||||||
using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
|
|
||||||
|
|
||||||
namespace Decoder {
|
namespace Decoder {
|
||||||
class H264;
|
class H264;
|
||||||
class VP8;
|
class VP8;
|
||||||
|
@ -51,7 +38,7 @@ public:
|
||||||
void Decode();
|
void Decode();
|
||||||
|
|
||||||
/// Returns next decoded frame
|
/// Returns next decoded frame
|
||||||
[[nodiscard]] AVFramePtr GetCurrentFrame();
|
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
|
||||||
|
|
||||||
/// Returns the value of current_codec
|
/// Returns the value of current_codec
|
||||||
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
|
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
|
||||||
|
@ -60,25 +47,9 @@ public:
|
||||||
[[nodiscard]] std::string_view GetCurrentCodecName() const;
|
[[nodiscard]] std::string_view GetCurrentCodecName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitializeAvCodecContext();
|
|
||||||
|
|
||||||
void InitializeAvFilters(AVFrame* frame);
|
|
||||||
|
|
||||||
void InitializeGpuDecoder();
|
|
||||||
|
|
||||||
bool CreateGpuAvDevice();
|
|
||||||
|
|
||||||
bool initialized{};
|
bool initialized{};
|
||||||
bool filters_initialized{};
|
|
||||||
Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
|
Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
|
||||||
|
FFmpeg::DecodeApi decode_api;
|
||||||
const AVCodec* av_codec{nullptr};
|
|
||||||
AVCodecContext* av_codec_ctx{nullptr};
|
|
||||||
AVBufferRef* av_gpu_decoder{nullptr};
|
|
||||||
|
|
||||||
AVFilterContext* av_filter_src_ctx{nullptr};
|
|
||||||
AVFilterContext* av_filter_sink_ctx{nullptr};
|
|
||||||
AVFilterGraph* av_filter_graph{nullptr};
|
|
||||||
|
|
||||||
Host1x::Host1x& host1x;
|
Host1x::Host1x& host1x;
|
||||||
const Host1x::NvdecCommon::NvdecRegisters& state;
|
const Host1x::NvdecCommon::NvdecRegisters& state;
|
||||||
|
@ -86,7 +57,7 @@ private:
|
||||||
std::unique_ptr<Decoder::VP8> vp8_decoder;
|
std::unique_ptr<Decoder::VP8> vp8_decoder;
|
||||||
std::unique_ptr<Decoder::VP9> vp9_decoder;
|
std::unique_ptr<Decoder::VP9> vp9_decoder;
|
||||||
|
|
||||||
std::queue<AVFramePtr> av_frames{};
|
std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|
|
@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
|
||||||
H264::~H264() = default;
|
H264::~H264() = default;
|
||||||
|
|
||||||
std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
|
std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
|
||||||
bool is_first_frame) {
|
size_t* out_configuration_size, bool is_first_frame) {
|
||||||
H264DecoderContext context;
|
H264DecoderContext context;
|
||||||
host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
|
host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
|
||||||
sizeof(H264DecoderContext));
|
sizeof(H264DecoderContext));
|
||||||
|
@ -39,6 +39,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
|
||||||
if (!is_first_frame && frame_number != 0) {
|
if (!is_first_frame && frame_number != 0) {
|
||||||
frame.resize_destructive(context.stream_len);
|
frame.resize_destructive(context.stream_len);
|
||||||
host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
|
host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
|
||||||
|
*out_configuration_size = 0;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +158,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
|
||||||
frame.resize(encoded_header.size() + context.stream_len);
|
frame.resize(encoded_header.size() + context.stream_len);
|
||||||
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
|
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
|
||||||
|
|
||||||
|
*out_configuration_size = encoded_header.size();
|
||||||
host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
|
host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
|
||||||
frame.data() + encoded_header.size(), context.stream_len);
|
frame.data() + encoded_header.size(), context.stream_len);
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ public:
|
||||||
|
|
||||||
/// Compose the H264 frame for FFmpeg decoding
|
/// Compose the H264 frame for FFmpeg decoding
|
||||||
[[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
|
[[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
|
||||||
|
size_t* out_configuration_size,
|
||||||
bool is_first_frame = false);
|
bool is_first_frame = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
419
src/video_core/host1x/ffmpeg/ffmpeg.cpp
Executable file
419
src/video_core/host1x/ffmpeg/ffmpeg.cpp
Executable file
|
@ -0,0 +1,419 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "video_core/host1x/ffmpeg/ffmpeg.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#ifdef LIBVA_FOUND
|
||||||
|
// for querying VAAPI driver information
|
||||||
|
#include <libavutil/hwcontext_vaapi.h>
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FFmpeg {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
|
||||||
|
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
|
||||||
|
constexpr std::array PreferredGpuDecoders = {
|
||||||
|
AV_HWDEVICE_TYPE_CUDA,
|
||||||
|
#ifdef _WIN32
|
||||||
|
AV_HWDEVICE_TYPE_D3D11VA,
|
||||||
|
AV_HWDEVICE_TYPE_DXVA2,
|
||||||
|
#elif defined(__unix__)
|
||||||
|
AV_HWDEVICE_TYPE_VAAPI,
|
||||||
|
AV_HWDEVICE_TYPE_VDPAU,
|
||||||
|
#endif
|
||||||
|
// last resort for Linux Flatpak (w/ NVIDIA)
|
||||||
|
AV_HWDEVICE_TYPE_VULKAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
|
||||||
|
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
|
||||||
|
if (*p == codec_context->pix_fmt) {
|
||||||
|
return codec_context->pix_fmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU");
|
||||||
|
av_buffer_unref(&codec_context->hw_device_ctx);
|
||||||
|
|
||||||
|
codec_context->pix_fmt = PreferredCpuFormat;
|
||||||
|
return codec_context->pix_fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AVError(int errnum) {
|
||||||
|
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
|
||||||
|
av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
|
||||||
|
return errbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Packet::Packet(std::span<const u8> data) {
|
||||||
|
m_packet = av_packet_alloc();
|
||||||
|
m_packet->data = const_cast<u8*>(data.data());
|
||||||
|
m_packet->size = static_cast<s32>(data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::~Packet() {
|
||||||
|
av_packet_free(&m_packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::Frame() {
|
||||||
|
m_frame = av_frame_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::~Frame() {
|
||||||
|
av_frame_free(&m_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||||
|
const AVCodecID av_codec = [&] {
|
||||||
|
switch (codec) {
|
||||||
|
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
|
||||||
|
return AV_CODEC_ID_H264;
|
||||||
|
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
|
||||||
|
return AV_CODEC_ID_VP8;
|
||||||
|
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
|
||||||
|
return AV_CODEC_ID_VP9;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unknown codec {}", codec);
|
||||||
|
return AV_CODEC_ID_NONE;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
m_codec = avcodec_find_decoder(av_codec);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
|
||||||
|
if (!config) {
|
||||||
|
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name,
|
||||||
|
av_hwdevice_get_type_name(type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 &&
|
||||||
|
config->device_type == type) {
|
||||||
|
LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
||||||
|
*out_pix_fmt = config->pix_fmt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
|
||||||
|
std::vector<AVHWDeviceType> types;
|
||||||
|
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
current_device_type = av_hwdevice_iterate_types(current_device_type);
|
||||||
|
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
types.push_back(current_device_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HardwareContext::~HardwareContext() {
|
||||||
|
av_buffer_unref(&m_gpu_decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
|
||||||
|
const Decoder& decoder) {
|
||||||
|
const auto supported_types = GetSupportedDeviceTypes();
|
||||||
|
for (const auto type : PreferredGpuDecoders) {
|
||||||
|
AVPixelFormat hw_pix_fmt;
|
||||||
|
|
||||||
|
if (std::ranges::find(supported_types, type) == supported_types.end()) {
|
||||||
|
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->InitializeWithType(type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
|
||||||
|
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
|
||||||
|
av_buffer_unref(&m_gpu_decoder);
|
||||||
|
|
||||||
|
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0);
|
||||||
|
ret < 0) {
|
||||||
|
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type),
|
||||||
|
AVError(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIBVA_FOUND
|
||||||
|
if (type == AV_HWDEVICE_TYPE_VAAPI) {
|
||||||
|
// We need to determine if this is an impersonated VAAPI driver.
|
||||||
|
auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
|
||||||
|
auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
|
||||||
|
const char* vendor_name = vaQueryVendorString(vactx->display);
|
||||||
|
if (strstr(vendor_name, "VDPAU backend")) {
|
||||||
|
// VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
|
||||||
|
LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
|
||||||
|
// Log the driver name just in case.
|
||||||
|
LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecoderContext::DecoderContext(const Decoder& decoder) {
|
||||||
|
m_codec_context = avcodec_alloc_context3(decoder.GetCodec());
|
||||||
|
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
|
||||||
|
m_codec_context->thread_count = 0;
|
||||||
|
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecoderContext::~DecoderContext() {
|
||||||
|
av_buffer_unref(&m_codec_context->hw_device_ctx);
|
||||||
|
avcodec_free_context(&m_codec_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context,
|
||||||
|
AVPixelFormat hw_pix_fmt) {
|
||||||
|
m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
|
||||||
|
m_codec_context->get_format = GetGpuFormat;
|
||||||
|
m_codec_context->pix_fmt = hw_pix_fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DecoderContext::OpenContext(const Decoder& decoder) {
|
||||||
|
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
|
||||||
|
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_codec_context->hw_device_ctx) {
|
||||||
|
LOG_INFO(HW_GPU, "Using FFmpeg software decoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DecoderContext::SendPacket(const Packet& packet) {
|
||||||
|
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
|
||||||
|
LOG_DEBUG(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
|
||||||
|
auto dst_frame = std::make_unique<Frame>();
|
||||||
|
|
||||||
|
const auto ReceiveImpl = [&](AVFrame* frame) {
|
||||||
|
if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
|
||||||
|
LOG_DEBUG(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_is_interlaced = frame->interlaced_frame != 0;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_codec_context->hw_device_ctx) {
|
||||||
|
// If we have a hardware context, make a separate frame here to receive the
|
||||||
|
// hardware result before sending it to the output.
|
||||||
|
Frame intermediate_frame;
|
||||||
|
|
||||||
|
if (!ReceiveImpl(intermediate_frame.GetFrame())) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_frame->SetFormat(PreferredGpuFormat);
|
||||||
|
if (const int ret =
|
||||||
|
av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
|
||||||
|
ret < 0) {
|
||||||
|
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, decode the frame as normal.
|
||||||
|
if (!ReceiveImpl(dst_frame->GetFrame())) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
|
||||||
|
const AVFilter* buffer_src = avfilter_get_by_name("buffer");
|
||||||
|
const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
|
||||||
|
AVFilterInOut* inputs = avfilter_inout_alloc();
|
||||||
|
AVFilterInOut* outputs = avfilter_inout_alloc();
|
||||||
|
SCOPE_EXIT({
|
||||||
|
avfilter_inout_free(&inputs);
|
||||||
|
avfilter_inout_free(&outputs);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't know how to get the accurate time_base but it doesn't matter for yadif filter
|
||||||
|
// so just use 1/1 to make buffer filter happy
|
||||||
|
std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
|
||||||
|
frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));
|
||||||
|
|
||||||
|
m_filter_graph = avfilter_graph_alloc();
|
||||||
|
int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
|
||||||
|
nullptr, m_filter_graph);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
|
||||||
|
m_filter_graph);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs->name = av_strdup("out");
|
||||||
|
inputs->filter_ctx = m_sink_context;
|
||||||
|
inputs->pad_idx = 0;
|
||||||
|
inputs->next = nullptr;
|
||||||
|
|
||||||
|
outputs->name = av_strdup("in");
|
||||||
|
outputs->filter_ctx = m_source_context;
|
||||||
|
outputs->pad_idx = 0;
|
||||||
|
outputs->next = nullptr;
|
||||||
|
|
||||||
|
const char* description = "yadif=1:-1:0";
|
||||||
|
ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = avfilter_graph_config(m_filter_graph, nullptr);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
|
||||||
|
if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
|
||||||
|
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
|
ret < 0) {
|
||||||
|
LOG_DEBUG(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
|
||||||
|
auto dst_frame = std::make_unique<Frame>();
|
||||||
|
const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());
|
||||||
|
|
||||||
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_DEBUG(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeinterlaceFilter::~DeinterlaceFilter() {
|
||||||
|
avfilter_graph_free(&m_filter_graph);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecodeApi::Reset() {
|
||||||
|
m_deinterlace_filter.reset();
|
||||||
|
m_hardware_context.reset();
|
||||||
|
m_decoder_context.reset();
|
||||||
|
m_decoder.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||||
|
this->Reset();
|
||||||
|
m_decoder.emplace(codec);
|
||||||
|
m_decoder_context.emplace(*m_decoder);
|
||||||
|
|
||||||
|
// Enable GPU decoding if requested.
|
||||||
|
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||||
|
m_hardware_context.emplace();
|
||||||
|
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the decoder context.
|
||||||
|
if (!m_decoder_context->OpenContext(*m_decoder)) {
|
||||||
|
this->Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) {
|
||||||
|
FFmpeg::Packet packet(packet_data);
|
||||||
|
return m_decoder_context->SendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
|
||||||
|
// Receive raw frame from decoder.
|
||||||
|
bool is_interlaced;
|
||||||
|
auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
|
||||||
|
if (!frame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_interlaced) {
|
||||||
|
// If the frame is not interlaced, we can pend it now.
|
||||||
|
frame_queue.push(std::move(frame));
|
||||||
|
} else {
|
||||||
|
// Create the deinterlacer if needed.
|
||||||
|
if (!m_deinterlace_filter) {
|
||||||
|
m_deinterlace_filter.emplace(*frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the frame we just received.
|
||||||
|
if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pend output fields.
|
||||||
|
while (true) {
|
||||||
|
auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
|
||||||
|
if (!filter_frame) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_queue.push(std::move(filter_frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FFmpeg
|
210
src/video_core/host1x/ffmpeg/ffmpeg.h
Executable file
210
src/video_core/host1x/ffmpeg/ffmpeg.h
Executable file
|
@ -0,0 +1,210 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/host1x/nvdec_common.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wconversion"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavfilter/avfilter.h>
|
||||||
|
#include <libavfilter/buffersink.h>
|
||||||
|
#include <libavfilter/buffersrc.h>
|
||||||
|
#include <libavutil/avutil.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FFmpeg {
|
||||||
|
|
||||||
|
class Packet;
|
||||||
|
class Frame;
|
||||||
|
class Decoder;
|
||||||
|
class HardwareContext;
|
||||||
|
class DecoderContext;
|
||||||
|
class DeinterlaceFilter;
|
||||||
|
|
||||||
|
// Wraps an AVPacket, a container for compressed bitstream data.
|
||||||
|
class Packet {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(Packet);
|
||||||
|
YUZU_NON_MOVEABLE(Packet);
|
||||||
|
|
||||||
|
explicit Packet(std::span<const u8> data);
|
||||||
|
~Packet();
|
||||||
|
|
||||||
|
AVPacket* GetPacket() const {
|
||||||
|
return m_packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVPacket* m_packet{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps an AVFrame, a container for audio and video stream data.
|
||||||
|
class Frame {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(Frame);
|
||||||
|
YUZU_NON_MOVEABLE(Frame);
|
||||||
|
|
||||||
|
explicit Frame();
|
||||||
|
~Frame();
|
||||||
|
|
||||||
|
int GetWidth() const {
|
||||||
|
return m_frame->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetHeight() const {
|
||||||
|
return m_frame->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVPixelFormat GetPixelFormat() const {
|
||||||
|
return static_cast<AVPixelFormat>(m_frame->format);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetStride(int plane) const {
|
||||||
|
return m_frame->linesize[plane];
|
||||||
|
}
|
||||||
|
|
||||||
|
int* GetStrides() const {
|
||||||
|
return m_frame->linesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* GetData(int plane) const {
|
||||||
|
return m_frame->data[plane];
|
||||||
|
}
|
||||||
|
|
||||||
|
u8** GetPlanes() const {
|
||||||
|
return m_frame->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFormat(int format) {
|
||||||
|
m_frame->format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVFrame* GetFrame() const {
|
||||||
|
return m_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVFrame* m_frame{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps an AVCodec, a type containing information about a codec.
|
||||||
|
class Decoder {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(Decoder);
|
||||||
|
YUZU_NON_MOVEABLE(Decoder);
|
||||||
|
|
||||||
|
explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec);
|
||||||
|
~Decoder() = default;
|
||||||
|
|
||||||
|
bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const;
|
||||||
|
|
||||||
|
const AVCodec* GetCodec() const {
|
||||||
|
return m_codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const AVCodec* m_codec{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps AVBufferRef for an accelerated decoder.
|
||||||
|
class HardwareContext {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(HardwareContext);
|
||||||
|
YUZU_NON_MOVEABLE(HardwareContext);
|
||||||
|
|
||||||
|
static std::vector<AVHWDeviceType> GetSupportedDeviceTypes();
|
||||||
|
|
||||||
|
explicit HardwareContext() = default;
|
||||||
|
~HardwareContext();
|
||||||
|
|
||||||
|
bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder);
|
||||||
|
|
||||||
|
AVBufferRef* GetBufferRef() const {
|
||||||
|
return m_gpu_decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool InitializeWithType(AVHWDeviceType type);
|
||||||
|
|
||||||
|
AVBufferRef* m_gpu_decoder{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps an AVCodecContext.
|
||||||
|
class DecoderContext {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(DecoderContext);
|
||||||
|
YUZU_NON_MOVEABLE(DecoderContext);
|
||||||
|
|
||||||
|
explicit DecoderContext(const Decoder& decoder);
|
||||||
|
~DecoderContext();
|
||||||
|
|
||||||
|
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
|
||||||
|
bool OpenContext(const Decoder& decoder);
|
||||||
|
bool SendPacket(const Packet& packet);
|
||||||
|
std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);
|
||||||
|
|
||||||
|
AVCodecContext* GetCodecContext() const {
|
||||||
|
return m_codec_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVCodecContext* m_codec_context{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps an AVFilterGraph.
|
||||||
|
class DeinterlaceFilter {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(DeinterlaceFilter);
|
||||||
|
YUZU_NON_MOVEABLE(DeinterlaceFilter);
|
||||||
|
|
||||||
|
explicit DeinterlaceFilter(const Frame& frame);
|
||||||
|
~DeinterlaceFilter();
|
||||||
|
|
||||||
|
bool AddSourceFrame(const Frame& frame);
|
||||||
|
std::unique_ptr<Frame> DrainSinkFrame();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVFilterGraph* m_filter_graph{};
|
||||||
|
AVFilterContext* m_source_context{};
|
||||||
|
AVFilterContext* m_sink_context{};
|
||||||
|
bool m_initialized{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class DecodeApi {
|
||||||
|
public:
|
||||||
|
DecodeApi() = default;
|
||||||
|
~DecodeApi() = default;
|
||||||
|
|
||||||
|
bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
bool SendPacket(std::span<const u8> packet_data, size_t configuration_size);
|
||||||
|
void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<FFmpeg::Decoder> m_decoder;
|
||||||
|
std::optional<FFmpeg::DecoderContext> m_decoder_context;
|
||||||
|
std::optional<FFmpeg::HardwareContext> m_hardware_context;
|
||||||
|
std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FFmpeg
|
|
@ -28,7 +28,7 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFramePtr Nvdec::GetFrame() {
|
std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() {
|
||||||
return codec->GetCurrentFrame();
|
return codec->GetCurrentFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ public:
|
||||||
void ProcessMethod(u32 method, u32 argument);
|
void ProcessMethod(u32 method, u32 argument);
|
||||||
|
|
||||||
/// Return most recently decoded frame
|
/// Return most recently decoded frame
|
||||||
[[nodiscard]] AVFramePtr GetFrame();
|
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Invoke codec to decode a frame
|
/// Invoke codec to decode a frame
|
||||||
|
|
|
@ -82,27 +82,26 @@ void Vic::Execute() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)};
|
const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)};
|
||||||
const AVFramePtr frame_ptr = nvdec_processor->GetFrame();
|
auto frame = nvdec_processor->GetFrame();
|
||||||
const auto* frame = frame_ptr.get();
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u64 surface_width = config.surface_width_minus1 + 1;
|
const u64 surface_width = config.surface_width_minus1 + 1;
|
||||||
const u64 surface_height = config.surface_height_minus1 + 1;
|
const u64 surface_height = config.surface_height_minus1 + 1;
|
||||||
if (static_cast<u64>(frame->width) != surface_width ||
|
if (static_cast<u64>(frame->GetWidth()) != surface_width ||
|
||||||
static_cast<u64>(frame->height) != surface_height) {
|
static_cast<u64>(frame->GetHeight()) != surface_height) {
|
||||||
// TODO: Properly support multiple video streams with differing frame dimensions
|
// TODO: Properly support multiple video streams with differing frame dimensions
|
||||||
LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}",
|
LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}",
|
||||||
frame->width, frame->height, surface_width, surface_height);
|
frame->GetWidth(), frame->GetHeight(), surface_width, surface_height);
|
||||||
}
|
}
|
||||||
switch (config.pixel_format) {
|
switch (config.pixel_format) {
|
||||||
case VideoPixelFormat::RGBA8:
|
case VideoPixelFormat::RGBA8:
|
||||||
case VideoPixelFormat::BGRA8:
|
case VideoPixelFormat::BGRA8:
|
||||||
case VideoPixelFormat::RGBX8:
|
case VideoPixelFormat::RGBX8:
|
||||||
WriteRGBFrame(frame, config);
|
WriteRGBFrame(std::move(frame), config);
|
||||||
break;
|
break;
|
||||||
case VideoPixelFormat::YUV420:
|
case VideoPixelFormat::YUV420:
|
||||||
WriteYUVFrame(frame, config);
|
WriteYUVFrame(std::move(frame), config);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value());
|
UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value());
|
||||||
|
@ -110,10 +109,14 @@ void Vic::Execute() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
|
void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
|
||||||
LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
|
LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
|
||||||
|
|
||||||
if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) {
|
const auto frame_width = frame->GetWidth();
|
||||||
|
const auto frame_height = frame->GetHeight();
|
||||||
|
const auto frame_format = frame->GetPixelFormat();
|
||||||
|
|
||||||
|
if (!scaler_ctx || frame_width != scaler_width || frame_height != scaler_height) {
|
||||||
const AVPixelFormat target_format = [pixel_format = config.pixel_format]() {
|
const AVPixelFormat target_format = [pixel_format = config.pixel_format]() {
|
||||||
switch (pixel_format) {
|
switch (pixel_format) {
|
||||||
case VideoPixelFormat::RGBA8:
|
case VideoPixelFormat::RGBA8:
|
||||||
|
@ -129,27 +132,26 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
|
||||||
|
|
||||||
sws_freeContext(scaler_ctx);
|
sws_freeContext(scaler_ctx);
|
||||||
// Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format
|
// Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format
|
||||||
scaler_ctx = sws_getContext(frame->width, frame->height,
|
scaler_ctx = sws_getContext(frame_width, frame_height, frame_format, frame_width,
|
||||||
static_cast<AVPixelFormat>(frame->format), frame->width,
|
frame_height, target_format, 0, nullptr, nullptr, nullptr);
|
||||||
frame->height, target_format, 0, nullptr, nullptr, nullptr);
|
scaler_width = frame_width;
|
||||||
scaler_width = frame->width;
|
scaler_height = frame_height;
|
||||||
scaler_height = frame->height;
|
|
||||||
converted_frame_buffer.reset();
|
converted_frame_buffer.reset();
|
||||||
}
|
}
|
||||||
if (!converted_frame_buffer) {
|
if (!converted_frame_buffer) {
|
||||||
const size_t frame_size = frame->width * frame->height * 4;
|
const size_t frame_size = frame_width * frame_height * 4;
|
||||||
converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free};
|
converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free};
|
||||||
}
|
}
|
||||||
const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0};
|
const std::array<int, 4> converted_stride{frame_width * 4, frame_height * 4, 0, 0};
|
||||||
u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
|
u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
|
||||||
sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr,
|
sws_scale(scaler_ctx, frame->GetPlanes(), frame->GetStrides(), 0, frame_height,
|
||||||
converted_stride.data());
|
&converted_frame_buf_addr, converted_stride.data());
|
||||||
|
|
||||||
// Use the minimum of surface/frame dimensions to avoid buffer overflow.
|
// Use the minimum of surface/frame dimensions to avoid buffer overflow.
|
||||||
const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1;
|
const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1;
|
||||||
const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1;
|
const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1;
|
||||||
const u32 width = std::min(surface_width, static_cast<u32>(frame->width));
|
const u32 width = std::min(surface_width, static_cast<u32>(frame_width));
|
||||||
const u32 height = std::min(surface_height, static_cast<u32>(frame->height));
|
const u32 height = std::min(surface_height, static_cast<u32>(frame_height));
|
||||||
const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
|
const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
|
||||||
if (blk_kind != 0) {
|
if (blk_kind != 0) {
|
||||||
// swizzle pitch linear to block linear
|
// swizzle pitch linear to block linear
|
||||||
|
@ -169,23 +171,23 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
|
void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
|
||||||
LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
|
LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
|
||||||
|
|
||||||
const std::size_t surface_width = config.surface_width_minus1 + 1;
|
const std::size_t surface_width = config.surface_width_minus1 + 1;
|
||||||
const std::size_t surface_height = config.surface_height_minus1 + 1;
|
const std::size_t surface_height = config.surface_height_minus1 + 1;
|
||||||
const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
|
const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
|
||||||
// Use the minimum of surface/frame dimensions to avoid buffer overflow.
|
// Use the minimum of surface/frame dimensions to avoid buffer overflow.
|
||||||
const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width));
|
const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->GetWidth()));
|
||||||
const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height));
|
const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->GetHeight()));
|
||||||
|
|
||||||
const auto stride = static_cast<size_t>(frame->linesize[0]);
|
const auto stride = static_cast<size_t>(frame->GetStride(0));
|
||||||
|
|
||||||
luma_buffer.resize_destructive(aligned_width * surface_height);
|
luma_buffer.resize_destructive(aligned_width * surface_height);
|
||||||
chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
|
chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
|
||||||
|
|
||||||
// Populate luma buffer
|
// Populate luma buffer
|
||||||
const u8* luma_src = frame->data[0];
|
const u8* luma_src = frame->GetData(0);
|
||||||
for (std::size_t y = 0; y < frame_height; ++y) {
|
for (std::size_t y = 0; y < frame_height; ++y) {
|
||||||
const std::size_t src = y * stride;
|
const std::size_t src = y * stride;
|
||||||
const std::size_t dst = y * aligned_width;
|
const std::size_t dst = y * aligned_width;
|
||||||
|
@ -196,16 +198,16 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
|
||||||
|
|
||||||
// Chroma
|
// Chroma
|
||||||
const std::size_t half_height = frame_height / 2;
|
const std::size_t half_height = frame_height / 2;
|
||||||
const auto half_stride = static_cast<size_t>(frame->linesize[1]);
|
const auto half_stride = static_cast<size_t>(frame->GetStride(1));
|
||||||
|
|
||||||
switch (frame->format) {
|
switch (frame->GetPixelFormat()) {
|
||||||
case AV_PIX_FMT_YUV420P: {
|
case AV_PIX_FMT_YUV420P: {
|
||||||
// Frame from FFmpeg software
|
// Frame from FFmpeg software
|
||||||
// Populate chroma buffer from both channels with interleaving.
|
// Populate chroma buffer from both channels with interleaving.
|
||||||
const std::size_t half_width = frame_width / 2;
|
const std::size_t half_width = frame_width / 2;
|
||||||
u8* chroma_buffer_data = chroma_buffer.data();
|
u8* chroma_buffer_data = chroma_buffer.data();
|
||||||
const u8* chroma_b_src = frame->data[1];
|
const u8* chroma_b_src = frame->GetData(1);
|
||||||
const u8* chroma_r_src = frame->data[2];
|
const u8* chroma_r_src = frame->GetData(2);
|
||||||
for (std::size_t y = 0; y < half_height; ++y) {
|
for (std::size_t y = 0; y < half_height; ++y) {
|
||||||
const std::size_t src = y * half_stride;
|
const std::size_t src = y * half_stride;
|
||||||
const std::size_t dst = y * aligned_width;
|
const std::size_t dst = y * aligned_width;
|
||||||
|
@ -219,7 +221,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
|
||||||
case AV_PIX_FMT_NV12: {
|
case AV_PIX_FMT_NV12: {
|
||||||
// Frame from VA-API hardware
|
// Frame from VA-API hardware
|
||||||
// This is already interleaved so just copy
|
// This is already interleaved so just copy
|
||||||
const u8* chroma_src = frame->data[1];
|
const u8* chroma_src = frame->GetData(1);
|
||||||
for (std::size_t y = 0; y < half_height; ++y) {
|
for (std::size_t y = 0; y < half_height; ++y) {
|
||||||
const std::size_t src = y * stride;
|
const std::size_t src = y * stride;
|
||||||
const std::size_t dst = y * aligned_width;
|
const std::size_t dst = y * aligned_width;
|
||||||
|
|
|
@ -39,9 +39,9 @@ public:
|
||||||
private:
|
private:
|
||||||
void Execute();
|
void Execute();
|
||||||
|
|
||||||
void WriteRGBFrame(const AVFrame* frame, const VicConfig& config);
|
void WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
|
||||||
|
|
||||||
void WriteYUVFrame(const AVFrame* frame, const VicConfig& config);
|
void WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
|
||||||
|
|
||||||
Host1x& host1x;
|
Host1x& host1x;
|
||||||
std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
|
std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
|
||||||
|
|
|
@ -38,8 +38,6 @@ add_executable(yuzu
|
||||||
compatdb.ui
|
compatdb.ui
|
||||||
compatibility_list.cpp
|
compatibility_list.cpp
|
||||||
compatibility_list.h
|
compatibility_list.h
|
||||||
configuration/config.cpp
|
|
||||||
configuration/config.h
|
|
||||||
configuration/configuration_shared.cpp
|
configuration/configuration_shared.cpp
|
||||||
configuration/configuration_shared.h
|
configuration/configuration_shared.h
|
||||||
configuration/configure.ui
|
configuration/configure.ui
|
||||||
|
@ -147,6 +145,8 @@ add_executable(yuzu
|
||||||
configuration/shared_translation.h
|
configuration/shared_translation.h
|
||||||
configuration/shared_widget.cpp
|
configuration/shared_widget.cpp
|
||||||
configuration/shared_widget.h
|
configuration/shared_widget.h
|
||||||
|
configuration/qt_config.cpp
|
||||||
|
configuration/qt_config.h
|
||||||
debugger/console.cpp
|
debugger/console.cpp
|
||||||
debugger/console.h
|
debugger/console.h
|
||||||
debugger/controller.cpp
|
debugger/controller.cpp
|
||||||
|
@ -344,7 +344,7 @@ endif()
|
||||||
|
|
||||||
create_target_directory_groups(yuzu)
|
create_target_directory_groups(yuzu)
|
||||||
|
|
||||||
target_link_libraries(yuzu PRIVATE common core input_common network video_core)
|
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets)
|
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
#include "core/hle/service/hid/controllers/npad.h"
|
#include "core/hle/service/hid/controllers/npad.h"
|
||||||
#include "core/hle/service/hid/hid.h"
|
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "ui_qt_controller.h"
|
#include "ui_qt_controller.h"
|
||||||
#include "yuzu/applets/qt_controller.h"
|
#include "yuzu/applets/qt_controller.h"
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
#include "input_common/drivers/camera.h"
|
#include "input_common/drivers/camera.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "ui_configure_camera.h"
|
#include "ui_configure_camera.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_camera.h"
|
#include "yuzu/configuration/configure_camera.h"
|
||||||
|
|
||||||
ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
|
ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "ui_configure.h"
|
#include "ui_configure.h"
|
||||||
#include "vk_device_info.h"
|
#include "vk_device_info.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_audio.h"
|
#include "yuzu/configuration/configure_audio.h"
|
||||||
#include "yuzu/configuration/configure_cpu.h"
|
#include "yuzu/configuration/configure_cpu.h"
|
||||||
#include "yuzu/configuration/configure_debug_tab.h"
|
#include "yuzu/configuration/configure_debug_tab.h"
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
#include "core/hid/emulated_controller.h"
|
#include "core/hid/emulated_controller.h"
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
|
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "ui_configure_hotkeys.h"
|
#include "ui_configure_hotkeys.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_hotkeys.h"
|
#include "yuzu/configuration/configure_hotkeys.h"
|
||||||
#include "yuzu/hotkeys.h"
|
#include "yuzu/hotkeys.h"
|
||||||
|
#include "yuzu/uisettings.h"
|
||||||
#include "yuzu/util/sequence_dialog/sequence_dialog.h"
|
#include "yuzu/util/sequence_dialog/sequence_dialog.h"
|
||||||
|
|
||||||
constexpr int name_column = 0;
|
constexpr int name_column = 0;
|
||||||
|
@ -62,18 +63,21 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
|
||||||
|
|
||||||
void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
|
void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
|
||||||
for (const auto& group : registry.hotkey_groups) {
|
for (const auto& group : registry.hotkey_groups) {
|
||||||
|
QString parent_item_data = QString::fromStdString(group.first);
|
||||||
auto* parent_item =
|
auto* parent_item =
|
||||||
new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
|
new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(parent_item_data)));
|
||||||
parent_item->setEditable(false);
|
parent_item->setEditable(false);
|
||||||
parent_item->setData(group.first);
|
parent_item->setData(parent_item_data);
|
||||||
for (const auto& hotkey : group.second) {
|
for (const auto& hotkey : group.second) {
|
||||||
auto* action =
|
QString hotkey_action_data = QString::fromStdString(hotkey.first);
|
||||||
new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
|
auto* action = new QStandardItem(
|
||||||
|
QCoreApplication::translate("Hotkeys", qPrintable(hotkey_action_data)));
|
||||||
auto* keyseq =
|
auto* keyseq =
|
||||||
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
|
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
|
||||||
auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
|
auto* controller_keyseq =
|
||||||
|
new QStandardItem(QString::fromStdString(hotkey.second.controller_keyseq));
|
||||||
action->setEditable(false);
|
action->setEditable(false);
|
||||||
action->setData(hotkey.first);
|
action->setData(hotkey_action_data);
|
||||||
keyseq->setEditable(false);
|
keyseq->setEditable(false);
|
||||||
controller_keyseq->setEditable(false);
|
controller_keyseq->setEditable(false);
|
||||||
parent_item->appendRow({action, keyseq, controller_keyseq});
|
parent_item->appendRow({action, keyseq, controller_keyseq});
|
||||||
|
@ -301,13 +305,13 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
|
||||||
const QStandardItem* controller_keyseq =
|
const QStandardItem* controller_keyseq =
|
||||||
parent->child(key_column_id, controller_column);
|
parent->child(key_column_id, controller_column);
|
||||||
for (auto& [group, sub_actions] : registry.hotkey_groups) {
|
for (auto& [group, sub_actions] : registry.hotkey_groups) {
|
||||||
if (group != parent->data())
|
if (group != parent->data().toString().toStdString())
|
||||||
continue;
|
continue;
|
||||||
for (auto& [action_name, hotkey] : sub_actions) {
|
for (auto& [action_name, hotkey] : sub_actions) {
|
||||||
if (action_name != action->data())
|
if (action_name != action->data().toString().toStdString())
|
||||||
continue;
|
continue;
|
||||||
hotkey.keyseq = QKeySequence(keyseq->text());
|
hotkey.keyseq = QKeySequence(keyseq->text());
|
||||||
hotkey.controller_keyseq = controller_keyseq->text();
|
hotkey.controller_keyseq = controller_keyseq->text().toStdString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,7 +323,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
|
||||||
void ConfigureHotkeys::RestoreDefaults() {
|
void ConfigureHotkeys::RestoreDefaults() {
|
||||||
for (int r = 0; r < model->rowCount(); ++r) {
|
for (int r = 0; r < model->rowCount(); ++r) {
|
||||||
const QStandardItem* parent = model->item(r, 0);
|
const QStandardItem* parent = model->item(r, 0);
|
||||||
const int hotkey_size = static_cast<int>(Config::default_hotkeys.size());
|
const int hotkey_size = static_cast<int>(UISettings::default_hotkeys.size());
|
||||||
|
|
||||||
if (hotkey_size != parent->rowCount()) {
|
if (hotkey_size != parent->rowCount()) {
|
||||||
QMessageBox::warning(this, tr("Invalid hotkey settings"),
|
QMessageBox::warning(this, tr("Invalid hotkey settings"),
|
||||||
|
@ -330,10 +334,11 @@ void ConfigureHotkeys::RestoreDefaults() {
|
||||||
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
|
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
|
||||||
model->item(r, 0)
|
model->item(r, 0)
|
||||||
->child(r2, hotkey_column)
|
->child(r2, hotkey_column)
|
||||||
->setText(Config::default_hotkeys[r2].shortcut.keyseq);
|
->setText(QString::fromStdString(UISettings::default_hotkeys[r2].shortcut.keyseq));
|
||||||
model->item(r, 0)
|
model->item(r, 0)
|
||||||
->child(r2, controller_column)
|
->child(r2, controller_column)
|
||||||
->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq);
|
->setText(QString::fromStdString(
|
||||||
|
UISettings::default_hotkeys[r2].shortcut.controller_keyseq));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +384,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
|
||||||
|
|
||||||
void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
|
void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
|
||||||
const QString& default_key_sequence =
|
const QString& default_key_sequence =
|
||||||
Config::default_hotkeys[index.row()].shortcut.controller_keyseq;
|
QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.controller_keyseq);
|
||||||
const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence);
|
const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence);
|
||||||
|
|
||||||
if (key_sequence_used && default_key_sequence != model->data(index).toString()) {
|
if (key_sequence_used && default_key_sequence != model->data(index).toString()) {
|
||||||
|
@ -393,7 +398,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
|
||||||
|
|
||||||
void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
|
void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
|
||||||
const QKeySequence& default_key_sequence = QKeySequence::fromString(
|
const QKeySequence& default_key_sequence = QKeySequence::fromString(
|
||||||
Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText);
|
QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.keyseq),
|
||||||
|
QKeySequence::NativeText);
|
||||||
const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
|
const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
|
||||||
|
|
||||||
if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) {
|
if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) {
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hid/emulated_controller.h"
|
#include "core/hid/emulated_controller.h"
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "ui_configure_input_per_game.h"
|
#include "ui_configure_input_per_game.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_input_per_game.h"
|
#include "yuzu/configuration/configure_input_per_game.h"
|
||||||
#include "yuzu/configuration/input_profiles.h"
|
#include "yuzu/configuration/input_profiles.h"
|
||||||
|
|
||||||
ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_,
|
ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, QtConfig* config_,
|
||||||
QWidget* parent)
|
QWidget* parent)
|
||||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()),
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()),
|
||||||
profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} {
|
profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} {
|
||||||
|
@ -110,6 +110,6 @@ void ConfigureInputPerGame::SaveConfiguration() {
|
||||||
// Clear all controls from the config in case the user reverted back to globals
|
// Clear all controls from the config in case the user reverted back to globals
|
||||||
config->ClearControlPlayerValues();
|
config->ClearControlPlayerValues();
|
||||||
for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
|
for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
|
||||||
config->SaveControlPlayerValue(index);
|
config->SaveQtControlPlayerValues(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "ui_configure_input_per_game.h"
|
#include "ui_configure_input_per_game.h"
|
||||||
#include "yuzu/configuration/input_profiles.h"
|
#include "yuzu/configuration/input_profiles.h"
|
||||||
|
#include "yuzu/configuration/qt_config.h"
|
||||||
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ class ConfigureInputPerGame : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigureInputPerGame(Core::System& system_, Config* config_,
|
explicit ConfigureInputPerGame(Core::System& system_, QtConfig* config_,
|
||||||
QWidget* parent = nullptr);
|
QWidget* parent = nullptr);
|
||||||
|
|
||||||
/// Load and Save configurations to settings file.
|
/// Load and Save configurations to settings file.
|
||||||
|
@ -41,5 +42,5 @@ private:
|
||||||
std::array<QComboBox*, 8> profile_comboboxes;
|
std::array<QComboBox*, 8> profile_comboboxes;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
Config* config;
|
QtConfig* config;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,15 +12,16 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
|
#include "configuration/qt_config.h"
|
||||||
#include "core/hid/emulated_controller.h"
|
#include "core/hid/emulated_controller.h"
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "input_common/drivers/keyboard.h"
|
#include "input_common/drivers/keyboard.h"
|
||||||
#include "input_common/drivers/mouse.h"
|
#include "input_common/drivers/mouse.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "ui_configure_input_player.h"
|
#include "ui_configure_input_player.h"
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_input_player.h"
|
#include "yuzu/configuration/configure_input_player.h"
|
||||||
#include "yuzu/configuration/configure_input_player_widget.h"
|
#include "yuzu/configuration/configure_input_player_widget.h"
|
||||||
#include "yuzu/configuration/configure_mouse_panning.h"
|
#include "yuzu/configuration/configure_mouse_panning.h"
|
||||||
|
@ -1397,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
|
||||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
|
||||||
emulated_controller->SetButtonParam(
|
emulated_controller->SetButtonParam(
|
||||||
button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
|
button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
|
||||||
Config::default_buttons[button_id])});
|
QtConfig::default_buttons[button_id])});
|
||||||
}
|
}
|
||||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
|
||||||
Common::ParamPackage analog_param{};
|
Common::ParamPackage analog_param{};
|
||||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
|
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
|
||||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||||
Config::default_analogs[analog_id][sub_button_id])};
|
QtConfig::default_analogs[analog_id][sub_button_id])};
|
||||||
SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
|
SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
|
analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
|
||||||
Config::default_stick_mod[analog_id]));
|
QtConfig::default_stick_mod[analog_id]));
|
||||||
emulated_controller->SetStickParam(analog_id, analog_param);
|
emulated_controller->SetStickParam(analog_id, analog_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
||||||
emulated_controller->SetMotionParam(
|
emulated_controller->SetMotionParam(
|
||||||
motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
|
motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
|
||||||
Config::default_motions[motion_id])});
|
QtConfig::default_motions[motion_id])});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If mouse is selected we want to override with mappings from the driver
|
// If mouse is selected we want to override with mappings from the driver
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/xts_archive.h"
|
#include "core/file_sys/xts_archive.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "ui_configure_per_game.h"
|
#include "ui_configure_per_game.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configuration_shared.h"
|
#include "yuzu/configuration/configuration_shared.h"
|
||||||
#include "yuzu/configuration/configure_audio.h"
|
#include "yuzu/configuration/configure_audio.h"
|
||||||
#include "yuzu/configuration/configure_cpu.h"
|
#include "yuzu/configuration/configure_cpu.h"
|
||||||
|
@ -50,8 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
|
||||||
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
|
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
|
||||||
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
|
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
|
||||||
: fmt::format("{:016X}", title_id);
|
: fmt::format("{:016X}", title_id);
|
||||||
game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
|
game_config = std::make_unique<QtConfig>(config_file_name, Config::ConfigType::PerGameConfig);
|
||||||
|
|
||||||
addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
|
addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
|
||||||
audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
|
audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
|
||||||
cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
|
cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
|
||||||
|
@ -108,7 +107,7 @@ void ConfigurePerGame::ApplyConfiguration() {
|
||||||
system.ApplySettings();
|
system.ApplySettings();
|
||||||
Settings::LogSettings();
|
Settings::LogSettings();
|
||||||
|
|
||||||
game_config->Save();
|
game_config->SaveAllValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurePerGame::changeEvent(QEvent* event) {
|
void ConfigurePerGame::changeEvent(QEvent* event) {
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
|
|
||||||
#include "configuration/shared_widget.h"
|
#include "configuration/shared_widget.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs_types.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "vk_device_info.h"
|
#include "vk_device_info.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configuration_shared.h"
|
#include "yuzu/configuration/configuration_shared.h"
|
||||||
|
#include "yuzu/configuration/qt_config.h"
|
||||||
#include "yuzu/configuration/shared_translation.h"
|
#include "yuzu/configuration/shared_translation.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -72,7 +73,7 @@ private:
|
||||||
|
|
||||||
QGraphicsScene* scene;
|
QGraphicsScene* scene;
|
||||||
|
|
||||||
std::unique_ptr<Config> game_config;
|
std::unique_ptr<QtConfig> game_config;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
std::unique_ptr<ConfigurationShared::Builder> builder;
|
std::unique_ptr<ConfigurationShared::Builder> builder;
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "core/file_sys/xts_archive.h"
|
#include "core/file_sys/xts_archive.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "ui_configure_per_game_addons.h"
|
#include "ui_configure_per_game_addons.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_input.h"
|
#include "yuzu/configuration/configure_input.h"
|
||||||
#include "yuzu/configuration/configure_per_game_addons.h"
|
#include "yuzu/configuration/configure_per_game_addons.h"
|
||||||
#include "yuzu/uisettings.h"
|
#include "yuzu/uisettings.h"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "configuration/qt_config.h"
|
||||||
#include "core/hid/emulated_controller.h"
|
#include "core/hid/emulated_controller.h"
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
#include "input_common/drivers/keyboard.h"
|
#include "input_common/drivers/keyboard.h"
|
||||||
|
@ -15,7 +16,6 @@
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "ui_configure_ringcon.h"
|
#include "ui_configure_ringcon.h"
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_ringcon.h"
|
#include "yuzu/configuration/configure_ringcon.h"
|
||||||
|
|
||||||
const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
|
const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
|
||||||
|
@ -270,7 +270,7 @@ void ConfigureRingController::LoadConfiguration() {
|
||||||
|
|
||||||
void ConfigureRingController::RestoreDefaults() {
|
void ConfigureRingController::RestoreDefaults() {
|
||||||
const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
|
const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
|
0, 0, QtConfig::default_ringcon_analogs[0], QtConfig::default_ringcon_analogs[1], 0, 0.05f);
|
||||||
emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
|
emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
|
||||||
UpdateUI();
|
UpdateUI();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/time/time_manager.h"
|
#include "core/hle/service/time/time_manager.h"
|
||||||
#include "ui_configure_system.h"
|
#include "ui_configure_system.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configuration_shared.h"
|
#include "yuzu/configuration/configuration_shared.h"
|
||||||
#include "yuzu/configuration/configure_system.h"
|
#include "yuzu/configuration/configure_system.h"
|
||||||
#include "yuzu/configuration/shared_widget.h"
|
#include "yuzu/configuration/shared_widget.h"
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "common/settings.h"
|
||||||
#include "ui_configure_touchscreen_advanced.h"
|
#include "ui_configure_touchscreen_advanced.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_touchscreen_advanced.h"
|
#include "yuzu/configuration/configure_touchscreen_advanced.h"
|
||||||
|
|
||||||
ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
|
ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
|
||||||
|
|
|
@ -164,7 +164,7 @@ ConfigureUi::~ConfigureUi() = default;
|
||||||
|
|
||||||
void ConfigureUi::ApplyConfiguration() {
|
void ConfigureUi::ApplyConfiguration() {
|
||||||
UISettings::values.theme =
|
UISettings::values.theme =
|
||||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString();
|
||||||
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
||||||
UISettings::values.show_compat = ui->show_compat->isChecked();
|
UISettings::values.show_compat = ui->show_compat->isChecked();
|
||||||
UISettings::values.show_size = ui->show_size->isChecked();
|
UISettings::values.show_size = ui->show_size->isChecked();
|
||||||
|
@ -191,9 +191,10 @@ void ConfigureUi::RequestGameListUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureUi::SetConfiguration() {
|
void ConfigureUi::SetConfiguration() {
|
||||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
ui->theme_combobox->setCurrentIndex(
|
||||||
|
ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
|
||||||
ui->language_combobox->setCurrentIndex(
|
ui->language_combobox->setCurrentIndex(
|
||||||
ui->language_combobox->findData(UISettings::values.language));
|
ui->language_combobox->findData(QString::fromStdString(UISettings::values.language)));
|
||||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
||||||
ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
|
ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
|
||||||
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
|
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "yuzu/configuration/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "yuzu/configuration/input_profiles.h"
|
#include "yuzu/configuration/input_profiles.h"
|
||||||
|
|
||||||
namespace FS = Common::FS;
|
namespace FS = Common::FS;
|
||||||
|
@ -44,7 +44,7 @@ InputProfiles::InputProfiles() {
|
||||||
if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
|
if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
|
||||||
map_profiles.insert_or_assign(
|
map_profiles.insert_or_assign(
|
||||||
name_without_ext,
|
name_without_ext,
|
||||||
std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile));
|
std::make_unique<QtConfig>(name_without_ext, Config::ConfigType::InputProfile));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -85,7 +85,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p
|
||||||
}
|
}
|
||||||
|
|
||||||
map_profiles.insert_or_assign(
|
map_profiles.insert_or_assign(
|
||||||
profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
|
profile_name, std::make_unique<QtConfig>(profile_name, Config::ConfigType::InputProfile));
|
||||||
|
|
||||||
return SaveProfile(profile_name, player_index);
|
return SaveProfile(profile_name, player_index);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
map_profiles[profile_name]->ReadControlPlayerValue(player_index);
|
map_profiles[profile_name]->ReadQtControlPlayerValues(player_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t pla
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
map_profiles[profile_name]->SaveControlPlayerValue(player_index);
|
map_profiles[profile_name]->SaveQtControlPlayerValues(player_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "configuration/qt_config.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
@ -30,5 +32,5 @@ public:
|
||||||
private:
|
private:
|
||||||
bool ProfileExistsInMap(const std::string& profile_name) const;
|
bool ProfileExistsInMap(const std::string& profile_name) const;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
|
std::unordered_map<std::string, std::unique_ptr<QtConfig>> map_profiles;
|
||||||
};
|
};
|
||||||
|
|
548
src/yuzu/configuration/qt_config.cpp
Executable file
548
src/yuzu/configuration/qt_config.cpp
Executable file
|
@ -0,0 +1,548 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "input_common/main.h"
|
||||||
|
#include "qt_config.h"
|
||||||
|
#include "uisettings.h"
|
||||||
|
|
||||||
|
const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = {
|
||||||
|
Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
|
||||||
|
Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
|
||||||
|
Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
|
||||||
|
Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
|
||||||
|
Qt::Key_Q, Qt::Key_E,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<int, Settings::NativeMotion::NumMotions> QtConfig::default_motions = {
|
||||||
|
Qt::Key_7,
|
||||||
|
Qt::Key_8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{
|
||||||
|
{
|
||||||
|
Qt::Key_W,
|
||||||
|
Qt::Key_S,
|
||||||
|
Qt::Key_A,
|
||||||
|
Qt::Key_D,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Qt::Key_I,
|
||||||
|
Qt::Key_K,
|
||||||
|
Qt::Key_J,
|
||||||
|
Qt::Key_L,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const std::array<int, 2> QtConfig::default_stick_mod = {
|
||||||
|
Qt::Key_Shift,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<int, 2> QtConfig::default_ringcon_analogs{{
|
||||||
|
Qt::Key_A,
|
||||||
|
Qt::Key_D,
|
||||||
|
}};
|
||||||
|
|
||||||
|
QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type)
|
||||||
|
: Config(config_type) {
|
||||||
|
Initialize(config_name);
|
||||||
|
if (config_type != ConfigType::InputProfile) {
|
||||||
|
ReadQtValues();
|
||||||
|
SaveQtValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtConfig::~QtConfig() {
|
||||||
|
if (global) {
|
||||||
|
QtConfig::SaveAllValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReloadAllValues() {
|
||||||
|
Reload();
|
||||||
|
ReadQtValues();
|
||||||
|
SaveQtValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveAllValues() {
|
||||||
|
Save();
|
||||||
|
SaveQtValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadQtValues() {
|
||||||
|
if (global) {
|
||||||
|
ReadUIValues();
|
||||||
|
}
|
||||||
|
ReadQtControlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix.append("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
const auto profile_name =
|
||||||
|
ReadStringSetting(std::string(player_prefix).append("profile_name"));
|
||||||
|
if (profile_name.empty()) {
|
||||||
|
// Use the global input config
|
||||||
|
player = Settings::values.players.GetValue(true)[player_index];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
auto& player_buttons = player.buttons[i];
|
||||||
|
|
||||||
|
player_buttons = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
|
||||||
|
if (player_buttons.empty()) {
|
||||||
|
player_buttons = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
auto& player_analogs = player.analogs[i];
|
||||||
|
|
||||||
|
player_analogs = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
|
||||||
|
if (player_analogs.empty()) {
|
||||||
|
player_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||||
|
auto& player_motions = player.motions[i];
|
||||||
|
|
||||||
|
player_motions = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
|
||||||
|
if (player_motions.empty()) {
|
||||||
|
player_motions = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadHidbusValues() {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
|
||||||
|
auto& ringcon_analogs = Settings::values.ringcon_analogs;
|
||||||
|
|
||||||
|
ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
|
||||||
|
if (ringcon_analogs.empty()) {
|
||||||
|
ringcon_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadDebugControlValues() {
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
|
||||||
|
|
||||||
|
debug_pad_buttons = ReadStringSetting(
|
||||||
|
std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
|
||||||
|
if (debug_pad_buttons.empty()) {
|
||||||
|
debug_pad_buttons = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
|
||||||
|
|
||||||
|
debug_pad_analogs = ReadStringSetting(
|
||||||
|
std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
|
||||||
|
if (debug_pad_analogs.empty()) {
|
||||||
|
debug_pad_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadQtControlValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
Settings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||||
|
ReadQtPlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ReadDebugControlValues();
|
||||||
|
ReadHidbusValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadPathValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
||||||
|
|
||||||
|
UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
|
||||||
|
UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath"));
|
||||||
|
UISettings::values.game_dir_deprecated =
|
||||||
|
ReadStringSetting(std::string("gameListRootDir"), std::string("."));
|
||||||
|
UISettings::values.game_dir_deprecated_deepscan =
|
||||||
|
ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false));
|
||||||
|
|
||||||
|
const int gamedirs_size = BeginArray(std::string("gamedirs"));
|
||||||
|
for (int i = 0; i < gamedirs_size; ++i) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
UISettings::GameDir game_dir;
|
||||||
|
game_dir.path = ReadStringSetting(std::string("path"));
|
||||||
|
game_dir.deep_scan =
|
||||||
|
ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false));
|
||||||
|
game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true));
|
||||||
|
UISettings::values.game_dirs.append(game_dir);
|
||||||
|
}
|
||||||
|
EndArray();
|
||||||
|
|
||||||
|
// Create NAND and SD card directories if empty, these are not removable through the UI,
|
||||||
|
// also carries over old game list settings if present
|
||||||
|
if (UISettings::values.game_dirs.empty()) {
|
||||||
|
UISettings::GameDir game_dir;
|
||||||
|
game_dir.path = std::string("SDMC");
|
||||||
|
game_dir.expanded = true;
|
||||||
|
UISettings::values.game_dirs.append(game_dir);
|
||||||
|
game_dir.path = std::string("UserNAND");
|
||||||
|
UISettings::values.game_dirs.append(game_dir);
|
||||||
|
game_dir.path = std::string("SysNAND");
|
||||||
|
UISettings::values.game_dirs.append(game_dir);
|
||||||
|
if (UISettings::values.game_dir_deprecated != std::string(".")) {
|
||||||
|
game_dir.path = UISettings::values.game_dir_deprecated;
|
||||||
|
game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
|
||||||
|
UISettings::values.game_dirs.append(game_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UISettings::values.recent_files =
|
||||||
|
QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
|
||||||
|
.split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
|
||||||
|
UISettings::values.language = ReadStringSetting(std::string("language"), std::string(""));
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadShortcutValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
|
||||||
|
|
||||||
|
for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) {
|
||||||
|
BeginGroup(group);
|
||||||
|
BeginGroup(name);
|
||||||
|
|
||||||
|
// No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
|
||||||
|
// for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
|
||||||
|
// a file dialog in windowed mode
|
||||||
|
UISettings::values.shortcuts.push_back(
|
||||||
|
{name,
|
||||||
|
group,
|
||||||
|
{ReadStringSetting(std::string("KeySeq"), shortcut.keyseq),
|
||||||
|
ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq),
|
||||||
|
shortcut.context,
|
||||||
|
ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}});
|
||||||
|
|
||||||
|
EndGroup(); // name
|
||||||
|
EndGroup(); // group
|
||||||
|
}
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadUIValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
|
||||||
|
|
||||||
|
UISettings::values.theme = ReadStringSetting(
|
||||||
|
std::string("theme"),
|
||||||
|
std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second));
|
||||||
|
|
||||||
|
ReadUIGamelistValues();
|
||||||
|
ReadUILayoutValues();
|
||||||
|
ReadPathValues();
|
||||||
|
ReadScreenshotValues();
|
||||||
|
ReadShortcutValues();
|
||||||
|
ReadMultiplayerValues();
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Ui);
|
||||||
|
ReadCategory(Settings::Category::UiGeneral);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadUIGamelistValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::UiGameList);
|
||||||
|
|
||||||
|
const int favorites_size = BeginArray("favorites");
|
||||||
|
for (int i = 0; i < favorites_size; i++) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
UISettings::values.favorited_ids.append(ReadIntegerSetting(std::string("program_id")));
|
||||||
|
}
|
||||||
|
EndArray();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadUILayoutValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::UiLayout);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadMultiplayerValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::Multiplayer);
|
||||||
|
|
||||||
|
// Read ban list back
|
||||||
|
int size = BeginArray(std::string("username_ban_list"));
|
||||||
|
UISettings::values.multiplayer_ban_list.first.resize(size);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
UISettings::values.multiplayer_ban_list.first[i] =
|
||||||
|
ReadStringSetting(std::string("username"), std::string(""));
|
||||||
|
}
|
||||||
|
EndArray();
|
||||||
|
|
||||||
|
size = BeginArray(std::string("ip_ban_list"));
|
||||||
|
UISettings::values.multiplayer_ban_list.second.resize(size);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
UISettings::values.multiplayer_ban_list.second[i] =
|
||||||
|
ReadStringSetting("username", std::string(""));
|
||||||
|
}
|
||||||
|
EndArray();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveQtValues() {
|
||||||
|
if (global) {
|
||||||
|
SaveUIValues();
|
||||||
|
}
|
||||||
|
SaveQtControlValues();
|
||||||
|
|
||||||
|
WriteToIni();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix = std::string("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig() && player.profile_name.empty()) {
|
||||||
|
// No custom profile selected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
|
||||||
|
player.buttons[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
|
||||||
|
player.analogs[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||||
|
WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
|
||||||
|
player.motions[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveDebugControlValues() {
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
|
||||||
|
Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
|
||||||
|
Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveHidbusValues() {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
|
||||||
|
WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
|
||||||
|
std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveQtControlValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
Settings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||||
|
SaveQtPlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveDebugControlValues();
|
||||||
|
SaveHidbusValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SavePathValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
|
||||||
|
|
||||||
|
WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
|
||||||
|
WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path);
|
||||||
|
BeginArray(std::string("gamedirs"));
|
||||||
|
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
const auto& game_dir = UISettings::values.game_dirs[i];
|
||||||
|
WriteSetting(std::string("path"), game_dir.path);
|
||||||
|
WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
|
||||||
|
WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
|
||||||
|
}
|
||||||
|
EndArray();
|
||||||
|
|
||||||
|
WriteSetting(std::string("recentFiles"),
|
||||||
|
UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
|
||||||
|
WriteSetting(std::string("language"), UISettings::values.language);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveShortcutValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
|
||||||
|
|
||||||
|
// Lengths of UISettings::values.shortcuts & default_hotkeys are same.
|
||||||
|
// However, their ordering must also be the same.
|
||||||
|
for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) {
|
||||||
|
const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
|
||||||
|
const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut;
|
||||||
|
|
||||||
|
BeginGroup(group);
|
||||||
|
BeginGroup(name);
|
||||||
|
|
||||||
|
WriteSetting(std::string("KeySeq"), shortcut.keyseq,
|
||||||
|
std::make_optional(default_hotkey.keyseq));
|
||||||
|
WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
|
||||||
|
std::make_optional(default_hotkey.controller_keyseq));
|
||||||
|
WriteSetting(std::string("Context"), shortcut.context,
|
||||||
|
std::make_optional(default_hotkey.context));
|
||||||
|
WriteSetting(std::string("Repeat"), shortcut.repeat,
|
||||||
|
std::make_optional(default_hotkey.repeat));
|
||||||
|
|
||||||
|
EndGroup(); // name
|
||||||
|
EndGroup(); // group
|
||||||
|
}
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveUIValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Ui);
|
||||||
|
WriteCategory(Settings::Category::UiGeneral);
|
||||||
|
|
||||||
|
WriteSetting(std::string("theme"), UISettings::values.theme,
|
||||||
|
std::make_optional(std::string(
|
||||||
|
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
|
||||||
|
|
||||||
|
SaveUIGamelistValues();
|
||||||
|
SaveUILayoutValues();
|
||||||
|
SavePathValues();
|
||||||
|
SaveScreenshotValues();
|
||||||
|
SaveShortcutValues();
|
||||||
|
SaveMultiplayerValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveUIGamelistValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::UiGameList);
|
||||||
|
|
||||||
|
BeginArray(std::string("favorites"));
|
||||||
|
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
|
||||||
|
SetArrayIndex(i);
|
||||||
|
WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
|
||||||
|
}
|
||||||
|
EndArray(); // favorites
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveUILayoutValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::UiLayout);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveMultiplayerValues() {
|
||||||
|
BeginGroup(std::string("Multiplayer"));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::Multiplayer);
|
||||||
|
|
||||||
|
// Write ban list
|
||||||
|
BeginArray(std::string("username_ban_list"));
|
||||||
|
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
|
||||||
|
SetArrayIndex(static_cast<int>(i));
|
||||||
|
WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]);
|
||||||
|
}
|
||||||
|
EndArray(); // username_ban_list
|
||||||
|
|
||||||
|
BeginArray(std::string("ip_ban_list"));
|
||||||
|
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
|
||||||
|
SetArrayIndex(static_cast<int>(i));
|
||||||
|
WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
|
||||||
|
}
|
||||||
|
EndArray(); // ip_ban_list
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
|
||||||
|
auto& map = Settings::values.linkage.by_category;
|
||||||
|
if (map.contains(category)) {
|
||||||
|
return Settings::values.linkage.by_category[category];
|
||||||
|
}
|
||||||
|
return UISettings::values.linkage.by_category[category];
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
ReadPlayerValues(player_index);
|
||||||
|
ReadQtPlayerValues(player_index);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
SavePlayerValues(player_index);
|
||||||
|
SaveQtPlayerValues(player_index);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
|
||||||
|
WriteToIni();
|
||||||
|
}
|
55
src/yuzu/configuration/qt_config.h
Executable file
55
src/yuzu/configuration/qt_config.h
Executable file
|
@ -0,0 +1,55 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
#include "frontend_common/config.h"
|
||||||
|
|
||||||
|
class QtConfig final : public Config {
|
||||||
|
public:
|
||||||
|
explicit QtConfig(const std::string& config_name = "qt-config",
|
||||||
|
ConfigType config_type = ConfigType::GlobalConfig);
|
||||||
|
~QtConfig() override;
|
||||||
|
|
||||||
|
void ReloadAllValues() override;
|
||||||
|
void SaveAllValues() override;
|
||||||
|
|
||||||
|
void ReadQtControlPlayerValues(std::size_t player_index);
|
||||||
|
void SaveQtControlPlayerValues(std::size_t player_index);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadQtValues();
|
||||||
|
void ReadQtPlayerValues(std::size_t player_index);
|
||||||
|
void ReadQtControlValues();
|
||||||
|
void ReadHidbusValues() override;
|
||||||
|
void ReadDebugControlValues() override;
|
||||||
|
void ReadPathValues() override;
|
||||||
|
void ReadShortcutValues() override;
|
||||||
|
void ReadUIValues() override;
|
||||||
|
void ReadUIGamelistValues() override;
|
||||||
|
void ReadUILayoutValues() override;
|
||||||
|
void ReadMultiplayerValues() override;
|
||||||
|
|
||||||
|
void SaveQtValues();
|
||||||
|
void SaveQtPlayerValues(std::size_t player_index);
|
||||||
|
void SaveQtControlValues();
|
||||||
|
void SaveHidbusValues() override;
|
||||||
|
void SaveDebugControlValues() override;
|
||||||
|
void SavePathValues() override;
|
||||||
|
void SaveShortcutValues() override;
|
||||||
|
void SaveUIValues() override;
|
||||||
|
void SaveUIGamelistValues() override;
|
||||||
|
void SaveUILayoutValues() override;
|
||||||
|
void SaveMultiplayerValues() override;
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||||
|
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
|
||||||
|
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||||
|
static const std::array<int, 2> default_stick_mod;
|
||||||
|
static const std::array<int, 2> default_ringcon_analogs;
|
||||||
|
};
|
|
@ -10,6 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
|
||||||
class QWidget;
|
class QWidget;
|
||||||
|
|
||||||
|
@ -22,4 +23,46 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
|
||||||
|
|
||||||
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
|
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
|
||||||
|
|
||||||
|
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
|
||||||
|
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
|
||||||
|
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
|
||||||
|
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
|
||||||
|
{Settings::ScalingFilter::NearestNeighbor,
|
||||||
|
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
|
||||||
|
{Settings::ScalingFilter::Bilinear,
|
||||||
|
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
|
||||||
|
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
|
||||||
|
{Settings::ScalingFilter::Gaussian,
|
||||||
|
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
|
||||||
|
{Settings::ScalingFilter::ScaleForce,
|
||||||
|
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
|
||||||
|
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
|
||||||
|
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
|
||||||
|
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
|
||||||
|
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
|
||||||
|
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
|
||||||
|
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
|
||||||
|
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
|
||||||
|
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
|
||||||
|
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
|
||||||
|
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
|
||||||
|
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
|
||||||
|
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ConfigurationShared
|
} // namespace ConfigurationShared
|
||||||
|
|
|
@ -36,10 +36,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
|
||||||
|
|
||||||
bool IsDarkTheme() {
|
bool IsDarkTheme() {
|
||||||
const auto& theme = UISettings::values.theme;
|
const auto& theme = UISettings::values.theme;
|
||||||
return theme == QStringLiteral("qdarkstyle") ||
|
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
|
||||||
theme == QStringLiteral("qdarkstyle_midnight_blue") ||
|
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
|
||||||
theme == QStringLiteral("colorful_dark") ||
|
|
||||||
theme == QStringLiteral("colorful_midnight_blue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -278,7 +278,7 @@ void GameList::OnUpdateThemedIcons() {
|
||||||
case GameListItemType::CustomDir: {
|
case GameListItemType::CustomDir: {
|
||||||
const UISettings::GameDir& game_dir =
|
const UISettings::GameDir& game_dir =
|
||||||
UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
|
UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
|
||||||
const QString icon_name = QFileInfo::exists(game_dir.path)
|
const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path))
|
||||||
? QStringLiteral("folder")
|
? QStringLiteral("folder")
|
||||||
: QStringLiteral("bad_folder");
|
: QStringLiteral("bad_folder");
|
||||||
child->setData(
|
child->setData(
|
||||||
|
@ -727,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
|
connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
|
||||||
emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path);
|
emit OpenDirectory(
|
||||||
|
QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,7 +870,7 @@ const QStringList GameList::supported_file_extensions = {
|
||||||
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
|
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
|
||||||
|
|
||||||
void GameList::RefreshGameDirectory() {
|
void GameList::RefreshGameDirectory() {
|
||||||
if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) {
|
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
|
||||||
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
||||||
PopulateAsync(UISettings::values.game_dirs);
|
PopulateAsync(UISettings::values.game_dirs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,13 +286,13 @@ public:
|
||||||
setData(QObject::tr("System Titles"), Qt::DisplayRole);
|
setData(QObject::tr("System Titles"), Qt::DisplayRole);
|
||||||
break;
|
break;
|
||||||
case GameListItemType::CustomDir: {
|
case GameListItemType::CustomDir: {
|
||||||
const QString icon_name = QFileInfo::exists(game_dir->path)
|
QString path = QString::fromStdString(game_dir->path);
|
||||||
? QStringLiteral("folder")
|
const QString icon_name =
|
||||||
: QStringLiteral("bad_folder");
|
QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder");
|
||||||
setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
|
setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
|
||||||
icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||||
Qt::DecorationRole);
|
Qt::DecorationRole);
|
||||||
setData(game_dir->path, Qt::DisplayRole);
|
setData(path, Qt::DisplayRole);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -456,26 +456,26 @@ void GameListWorker::run() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game_dir.path == QStringLiteral("SDMC")) {
|
if (game_dir.path == std::string("SDMC")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
||||||
DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else if (game_dir.path == QStringLiteral("UserNAND")) {
|
} else if (game_dir.path == std::string("UserNAND")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
||||||
DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else if (game_dir.path == QStringLiteral("SysNAND")) {
|
} else if (game_dir.path == std::string("SysNAND")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
||||||
DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else {
|
} else {
|
||||||
watch_list.append(game_dir.path);
|
watch_list.append(QString::fromStdString(game_dir.path));
|
||||||
auto* const game_list_dir = new GameListDir(game_dir);
|
auto* const game_list_dir = new GameListDir(game_dir);
|
||||||
DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
|
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
|
||||||
game_dir.deep_scan, game_list_dir);
|
game_list_dir);
|
||||||
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
|
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
|
||||||
game_dir.deep_scan, game_list_dir);
|
game_list_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ void HotkeyRegistry::SaveHotkeys() {
|
||||||
for (const auto& hotkey : group.second) {
|
for (const auto& hotkey : group.second) {
|
||||||
UISettings::values.shortcuts.push_back(
|
UISettings::values.shortcuts.push_back(
|
||||||
{hotkey.first, group.first,
|
{hotkey.first, group.first,
|
||||||
UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
|
UISettings::ContextualShortcut({hotkey.second.keyseq.toString().toStdString(),
|
||||||
hotkey.second.controller_keyseq,
|
hotkey.second.controller_keyseq,
|
||||||
hotkey.second.context, hotkey.second.repeat})});
|
hotkey.second.context, hotkey.second.repeat})});
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,12 @@ void HotkeyRegistry::LoadHotkeys() {
|
||||||
// beginGroup()
|
// beginGroup()
|
||||||
for (auto shortcut : UISettings::values.shortcuts) {
|
for (auto shortcut : UISettings::values.shortcuts) {
|
||||||
Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
|
Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
|
||||||
if (!shortcut.shortcut.keyseq.isEmpty()) {
|
if (!shortcut.shortcut.keyseq.empty()) {
|
||||||
hk.keyseq =
|
hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq),
|
||||||
QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText);
|
QKeySequence::NativeText);
|
||||||
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
|
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
|
||||||
}
|
}
|
||||||
if (!shortcut.shortcut.controller_keyseq.isEmpty()) {
|
if (!shortcut.shortcut.controller_keyseq.empty()) {
|
||||||
hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
|
hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
|
||||||
}
|
}
|
||||||
if (hk.shortcut) {
|
if (hk.shortcut) {
|
||||||
|
@ -51,7 +51,8 @@ void HotkeyRegistry::LoadHotkeys() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
|
QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string& action,
|
||||||
|
QWidget* widget) {
|
||||||
Hotkey& hk = hotkey_groups[group][action];
|
Hotkey& hk = hotkey_groups[group][action];
|
||||||
|
|
||||||
if (!hk.shortcut) {
|
if (!hk.shortcut) {
|
||||||
|
@ -62,7 +63,8 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
|
||||||
return hk.shortcut;
|
return hk.shortcut;
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action,
|
ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group,
|
||||||
|
const std::string& action,
|
||||||
Core::HID::EmulatedController* controller) {
|
Core::HID::EmulatedController* controller) {
|
||||||
Hotkey& hk = hotkey_groups[group][action];
|
Hotkey& hk = hotkey_groups[group][action];
|
||||||
|
|
||||||
|
@ -74,12 +76,12 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, co
|
||||||
return hk.controller_shortcut;
|
return hk.controller_shortcut;
|
||||||
}
|
}
|
||||||
|
|
||||||
QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) {
|
QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) {
|
||||||
return hotkey_groups[group][action].keyseq;
|
return hotkey_groups[group][action].keyseq;
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group,
|
Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const std::string& group,
|
||||||
const QString& action) {
|
const std::string& action) {
|
||||||
return hotkey_groups[group][action].context;
|
return hotkey_groups[group][action].context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +103,10 @@ void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) {
|
||||||
button_sequence = buttons;
|
button_sequence = buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControllerShortcut::SetKey(const QString& buttons_shortcut) {
|
void ControllerShortcut::SetKey(const std::string& buttons_shortcut) {
|
||||||
ControllerButtonSequence sequence{};
|
ControllerButtonSequence sequence{};
|
||||||
name = buttons_shortcut.toStdString();
|
name = buttons_shortcut;
|
||||||
std::istringstream command_line(buttons_shortcut.toStdString());
|
std::istringstream command_line(buttons_shortcut);
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(command_line, line, '+')) {
|
while (std::getline(command_line, line, '+')) {
|
||||||
if (line.empty()) {
|
if (line.empty()) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
~ControllerShortcut();
|
~ControllerShortcut();
|
||||||
|
|
||||||
void SetKey(const ControllerButtonSequence& buttons);
|
void SetKey(const ControllerButtonSequence& buttons);
|
||||||
void SetKey(const QString& buttons_shortcut);
|
void SetKey(const std::string& buttons_shortcut);
|
||||||
|
|
||||||
ControllerButtonSequence ButtonSequence() const;
|
ControllerButtonSequence ButtonSequence() const;
|
||||||
|
|
||||||
|
@ -88,8 +88,8 @@ public:
|
||||||
* will be the same. Thus, you shouldn't rely on the caller really being the
|
* will be the same. Thus, you shouldn't rely on the caller really being the
|
||||||
* QShortcut's parent.
|
* QShortcut's parent.
|
||||||
*/
|
*/
|
||||||
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
|
QShortcut* GetHotkey(const std::string& group, const std::string& action, QWidget* widget);
|
||||||
ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action,
|
ControllerShortcut* GetControllerHotkey(const std::string& group, const std::string& action,
|
||||||
Core::HID::EmulatedController* controller);
|
Core::HID::EmulatedController* controller);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +98,7 @@ public:
|
||||||
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
|
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
|
||||||
* @param action Name of the action (e.g. "Start Emulation", "Load Image").
|
* @param action Name of the action (e.g. "Start Emulation", "Load Image").
|
||||||
*/
|
*/
|
||||||
QKeySequence GetKeySequence(const QString& group, const QString& action);
|
QKeySequence GetKeySequence(const std::string& group, const std::string& action);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Qt::ShortcutContext object who can be connected to other
|
* Returns a Qt::ShortcutContext object who can be connected to other
|
||||||
|
@ -108,20 +108,20 @@ public:
|
||||||
* "Debugger").
|
* "Debugger").
|
||||||
* @param action Name of the action (e.g. "Start Emulation", "Load Image").
|
* @param action Name of the action (e.g. "Start Emulation", "Load Image").
|
||||||
*/
|
*/
|
||||||
Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
|
Qt::ShortcutContext GetShortcutContext(const std::string& group, const std::string& action);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Hotkey {
|
struct Hotkey {
|
||||||
QKeySequence keyseq;
|
QKeySequence keyseq;
|
||||||
QString controller_keyseq;
|
std::string controller_keyseq;
|
||||||
QShortcut* shortcut = nullptr;
|
QShortcut* shortcut = nullptr;
|
||||||
ControllerShortcut* controller_shortcut = nullptr;
|
ControllerShortcut* controller_shortcut = nullptr;
|
||||||
Qt::ShortcutContext context = Qt::WindowShortcut;
|
Qt::ShortcutContext context = Qt::WindowShortcut;
|
||||||
bool repeat;
|
bool repeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
using HotkeyMap = std::map<QString, Hotkey>;
|
using HotkeyMap = std::map<std::string, Hotkey>;
|
||||||
using HotkeyGroupMap = std::map<QString, HotkeyMap>;
|
using HotkeyGroupMap = std::map<std::string, HotkeyMap>;
|
||||||
|
|
||||||
HotkeyGroupMap hotkey_groups;
|
HotkeyGroupMap hotkey_groups;
|
||||||
};
|
};
|
||||||
|
|
|
@ -128,6 +128,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/perf_stats.h"
|
#include "core/perf_stats.h"
|
||||||
#include "core/telemetry_session.h"
|
#include "core/telemetry_session.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "input_common/drivers/tas_input.h"
|
#include "input_common/drivers/tas_input.h"
|
||||||
#include "input_common/drivers/virtual_amiibo.h"
|
#include "input_common/drivers/virtual_amiibo.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
|
@ -140,9 +141,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
#include "yuzu/compatdb.h"
|
#include "yuzu/compatdb.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
#include "yuzu/configuration/config.h"
|
|
||||||
#include "yuzu/configuration/configure_dialog.h"
|
#include "yuzu/configuration/configure_dialog.h"
|
||||||
#include "yuzu/configuration/configure_input_per_game.h"
|
#include "yuzu/configuration/configure_input_per_game.h"
|
||||||
|
#include "yuzu/configuration/qt_config.h"
|
||||||
#include "yuzu/debugger/console.h"
|
#include "yuzu/debugger/console.h"
|
||||||
#include "yuzu/debugger/controller.h"
|
#include "yuzu/debugger/controller.h"
|
||||||
#include "yuzu/debugger/profiler.h"
|
#include "yuzu/debugger/profiler.h"
|
||||||
|
@ -311,7 +312,7 @@ bool GMainWindow::CheckDarkMode() {
|
||||||
#endif // __unix__
|
#endif // __unix__
|
||||||
}
|
}
|
||||||
|
|
||||||
GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan)
|
GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
|
||||||
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
||||||
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
|
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
|
||||||
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
|
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
|
||||||
|
@ -676,7 +677,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
|
||||||
// Don't forget to apply settings.
|
// Don't forget to apply settings.
|
||||||
system->HIDCore().DisableAllControllerConfiguration();
|
system->HIDCore().DisableAllControllerConfiguration();
|
||||||
system->ApplySettings();
|
system->ApplySettings();
|
||||||
config->Save();
|
config->SaveAllValues();
|
||||||
|
|
||||||
UpdateStatusButtons();
|
UpdateStatusButtons();
|
||||||
|
|
||||||
|
@ -1129,7 +1130,7 @@ void GMainWindow::InitializeWidgets() {
|
||||||
connect(aa_status_button, &QPushButton::customContextMenuRequested,
|
connect(aa_status_button, &QPushButton::customContextMenuRequested,
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) {
|
for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) {
|
||||||
context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] {
|
context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] {
|
||||||
Settings::values.anti_aliasing.SetValue(aa_text_pair.first);
|
Settings::values.anti_aliasing.SetValue(aa_text_pair.first);
|
||||||
UpdateAAText();
|
UpdateAAText();
|
||||||
|
@ -1153,7 +1154,7 @@ void GMainWindow::InitializeWidgets() {
|
||||||
connect(filter_status_button, &QPushButton::customContextMenuRequested,
|
connect(filter_status_button, &QPushButton::customContextMenuRequested,
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
for (auto const& filter_text_pair : Config::scaling_filter_texts_map) {
|
for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) {
|
||||||
context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] {
|
context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] {
|
||||||
Settings::values.scaling_filter.SetValue(filter_text_pair.first);
|
Settings::values.scaling_filter.SetValue(filter_text_pair.first);
|
||||||
UpdateFilterText();
|
UpdateFilterText();
|
||||||
|
@ -1176,7 +1177,7 @@ void GMainWindow::InitializeWidgets() {
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
|
|
||||||
for (auto const& pair : Config::use_docked_mode_texts_map) {
|
for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
|
||||||
context_menu.addAction(pair.second, [this, &pair] {
|
context_menu.addAction(pair.second, [this, &pair] {
|
||||||
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
|
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
|
||||||
OnToggleDockedMode();
|
OnToggleDockedMode();
|
||||||
|
@ -1200,7 +1201,7 @@ void GMainWindow::InitializeWidgets() {
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
|
|
||||||
for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) {
|
for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
|
||||||
if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
|
if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1229,7 +1230,8 @@ void GMainWindow::InitializeWidgets() {
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
|
|
||||||
for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) {
|
for (auto const& renderer_backend_pair :
|
||||||
|
ConfigurationShared::renderer_backend_texts_map) {
|
||||||
if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
|
if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1294,16 +1296,17 @@ void GMainWindow::InitializeRecentFileMenuActions() {
|
||||||
|
|
||||||
void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
|
void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
|
||||||
const bool tas_allowed) {
|
const bool tas_allowed) {
|
||||||
static const QString main_window = QStringLiteral("Main Window");
|
static const auto main_window = std::string("Main Window");
|
||||||
action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name));
|
action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString()));
|
||||||
action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name));
|
action->setShortcutContext(
|
||||||
|
hotkey_registry.GetShortcutContext(main_window, action_name.toStdString()));
|
||||||
action->setAutoRepeat(false);
|
action->setAutoRepeat(false);
|
||||||
|
|
||||||
this->addAction(action);
|
this->addAction(action);
|
||||||
|
|
||||||
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
const auto* controller_hotkey =
|
const auto* controller_hotkey =
|
||||||
hotkey_registry.GetControllerHotkey(main_window, action_name, controller);
|
hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller);
|
||||||
connect(
|
connect(
|
||||||
controller_hotkey, &ControllerShortcut::Activated, this,
|
controller_hotkey, &ControllerShortcut::Activated, this,
|
||||||
[action, tas_allowed, this] {
|
[action, tas_allowed, this] {
|
||||||
|
@ -1335,10 +1338,11 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
|
|
||||||
static const QString main_window = QStringLiteral("Main Window");
|
static const QString main_window = QStringLiteral("Main Window");
|
||||||
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
|
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
|
||||||
const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this);
|
const auto* hotkey =
|
||||||
|
hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this);
|
||||||
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
const auto* controller_hotkey =
|
const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(
|
||||||
hotkey_registry.GetControllerHotkey(main_window, action_name, controller);
|
main_window.toStdString(), action_name.toStdString(), controller);
|
||||||
connect(hotkey, &QShortcut::activated, this, function);
|
connect(hotkey, &QShortcut::activated, this, function);
|
||||||
connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
|
connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
@ -1918,7 +1922,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||||
// Save configurations
|
// Save configurations
|
||||||
UpdateUISettings();
|
UpdateUISettings();
|
||||||
game_list->SaveInterfaceLayout();
|
game_list->SaveInterfaceLayout();
|
||||||
config->Save();
|
config->SaveAllValues();
|
||||||
|
|
||||||
u64 title_id{0};
|
u64 title_id{0};
|
||||||
|
|
||||||
|
@ -1936,7 +1940,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||||
const auto config_file_name = title_id == 0
|
const auto config_file_name = title_id == 0
|
||||||
? Common::FS::PathToUTF8String(file_path.filename())
|
? Common::FS::PathToUTF8String(file_path.filename())
|
||||||
: fmt::format("{:016X}", title_id);
|
: fmt::format("{:016X}", title_id);
|
||||||
Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
||||||
system->HIDCore().ReloadInputDevices();
|
system->HIDCore().ReloadInputDevices();
|
||||||
system->ApplySettings();
|
system->ApplySettings();
|
||||||
}
|
}
|
||||||
|
@ -3135,7 +3139,7 @@ void GMainWindow::OnGameListAddDirectory() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UISettings::GameDir game_dir{dir_path, false, true};
|
UISettings::GameDir game_dir{dir_path.toStdString(), false, true};
|
||||||
if (!UISettings::values.game_dirs.contains(game_dir)) {
|
if (!UISettings::values.game_dirs.contains(game_dir)) {
|
||||||
UISettings::values.game_dirs.append(game_dir);
|
UISettings::values.game_dirs.append(game_dir);
|
||||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||||
|
@ -3181,14 +3185,14 @@ void GMainWindow::OnMenuLoadFile() {
|
||||||
"%1 is an identifier for the Switch executable file extensions.")
|
"%1 is an identifier for the Switch executable file extensions.")
|
||||||
.arg(extensions);
|
.arg(extensions);
|
||||||
const QString filename = QFileDialog::getOpenFileName(
|
const QString filename = QFileDialog::getOpenFileName(
|
||||||
this, tr("Load File"), UISettings::values.roms_path, file_filter);
|
this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter);
|
||||||
is_load_file_select_active = false;
|
is_load_file_select_active = false;
|
||||||
|
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UISettings::values.roms_path = QFileInfo(filename).path();
|
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||||
BootGame(filename);
|
BootGame(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3221,7 +3225,8 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
"Image (*.xci)");
|
"Image (*.xci)");
|
||||||
|
|
||||||
QStringList filenames = QFileDialog::getOpenFileNames(
|
QStringList filenames = QFileDialog::getOpenFileNames(
|
||||||
this, tr("Install Files"), UISettings::values.roms_path, file_filter);
|
this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path),
|
||||||
|
file_filter);
|
||||||
|
|
||||||
if (filenames.isEmpty()) {
|
if (filenames.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -3239,7 +3244,7 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save folder location of the first selected file
|
// Save folder location of the first selected file
|
||||||
UISettings::values.roms_path = QFileInfo(filenames[0]).path();
|
UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString();
|
||||||
|
|
||||||
int remaining = filenames.size();
|
int remaining = filenames.size();
|
||||||
|
|
||||||
|
@ -3584,7 +3589,7 @@ void GMainWindow::OnExit() {
|
||||||
|
|
||||||
void GMainWindow::OnSaveConfig() {
|
void GMainWindow::OnSaveConfig() {
|
||||||
system->ApplySettings();
|
system->ApplySettings();
|
||||||
config->Save();
|
config->SaveAllValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
|
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
|
||||||
|
@ -3840,7 +3845,7 @@ void GMainWindow::OnConfigure() {
|
||||||
|
|
||||||
Settings::values.disabled_addons.clear();
|
Settings::values.disabled_addons.clear();
|
||||||
|
|
||||||
config = std::make_unique<Config>();
|
config = std::make_unique<QtConfig>();
|
||||||
UISettings::values.reset_to_defaults = false;
|
UISettings::values.reset_to_defaults = false;
|
||||||
|
|
||||||
UISettings::values.game_dirs = std::move(old_game_dirs);
|
UISettings::values.game_dirs = std::move(old_game_dirs);
|
||||||
|
@ -3875,7 +3880,7 @@ void GMainWindow::OnConfigure() {
|
||||||
|
|
||||||
UISettings::values.configuration_applied = false;
|
UISettings::values.configuration_applied = false;
|
||||||
|
|
||||||
config->Save();
|
config->SaveAllValues();
|
||||||
|
|
||||||
if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
|
if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
|
||||||
render_window->installEventFilter(render_window);
|
render_window->installEventFilter(render_window);
|
||||||
|
@ -4091,7 +4096,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
|
||||||
UISettings::values.configuration_applied = false;
|
UISettings::values.configuration_applied = false;
|
||||||
|
|
||||||
if (!is_powered_on) {
|
if (!is_powered_on) {
|
||||||
config->Save();
|
config->SaveAllValues();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4324,7 +4329,7 @@ void GMainWindow::OnAlbum() {
|
||||||
system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer);
|
system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer);
|
||||||
|
|
||||||
const auto filename = QString::fromStdString(album_nca->GetFullPath());
|
const auto filename = QString::fromStdString(album_nca->GetFullPath());
|
||||||
UISettings::values.roms_path = QFileInfo(filename).path();
|
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||||
BootGame(filename, AlbumId);
|
BootGame(filename, AlbumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4348,7 +4353,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
|
||||||
system->GetAppletManager().SetCabinetMode(mode);
|
system->GetAppletManager().SetCabinetMode(mode);
|
||||||
|
|
||||||
const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
|
const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
|
||||||
UISettings::values.roms_path = QFileInfo(filename).path();
|
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||||
BootGame(filename, CabinetId);
|
BootGame(filename, CabinetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4371,7 +4376,7 @@ void GMainWindow::OnMiiEdit() {
|
||||||
system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit);
|
system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit);
|
||||||
|
|
||||||
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
|
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
|
||||||
UISettings::values.roms_path = QFileInfo(filename).path();
|
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||||
BootGame(filename, MiiEditId);
|
BootGame(filename, MiiEditId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4565,7 +4570,8 @@ void GMainWindow::UpdateStatusBar() {
|
||||||
|
|
||||||
void GMainWindow::UpdateGPUAccuracyButton() {
|
void GMainWindow::UpdateGPUAccuracyButton() {
|
||||||
const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
|
const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
|
||||||
const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
|
const auto gpu_accuracy_text =
|
||||||
|
ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
|
||||||
gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
|
gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
|
||||||
gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
|
gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
|
||||||
}
|
}
|
||||||
|
@ -4574,31 +4580,32 @@ void GMainWindow::UpdateDockedButton() {
|
||||||
const auto console_mode = Settings::values.use_docked_mode.GetValue();
|
const auto console_mode = Settings::values.use_docked_mode.GetValue();
|
||||||
dock_status_button->setChecked(Settings::IsDockedMode());
|
dock_status_button->setChecked(Settings::IsDockedMode());
|
||||||
dock_status_button->setText(
|
dock_status_button->setText(
|
||||||
Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
|
ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateAPIText() {
|
void GMainWindow::UpdateAPIText() {
|
||||||
const auto api = Settings::values.renderer_backend.GetValue();
|
const auto api = Settings::values.renderer_backend.GetValue();
|
||||||
const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second;
|
const auto renderer_status_text =
|
||||||
|
ConfigurationShared::renderer_backend_texts_map.find(api)->second;
|
||||||
renderer_status_button->setText(
|
renderer_status_button->setText(
|
||||||
api == Settings::RendererBackend::OpenGL
|
api == Settings::RendererBackend::OpenGL
|
||||||
? tr("%1 %2").arg(
|
? tr("%1 %2").arg(renderer_status_text.toUpper(),
|
||||||
renderer_status_text.toUpper(),
|
ConfigurationShared::shader_backend_texts_map
|
||||||
Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue())
|
.find(Settings::values.shader_backend.GetValue())
|
||||||
->second)
|
->second)
|
||||||
: renderer_status_text.toUpper());
|
: renderer_status_text.toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateFilterText() {
|
void GMainWindow::UpdateFilterText() {
|
||||||
const auto filter = Settings::values.scaling_filter.GetValue();
|
const auto filter = Settings::values.scaling_filter.GetValue();
|
||||||
const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second;
|
const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second;
|
||||||
filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
|
filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
|
||||||
: filter_text.toUpper());
|
: filter_text.toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateAAText() {
|
void GMainWindow::UpdateAAText() {
|
||||||
const auto aa_mode = Settings::values.anti_aliasing.GetValue();
|
const auto aa_mode = Settings::values.anti_aliasing.GetValue();
|
||||||
const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second;
|
const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second;
|
||||||
aa_status_button->setText(aa_mode == Settings::AntiAliasing::None
|
aa_status_button->setText(aa_mode == Settings::AntiAliasing::None
|
||||||
? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA"))
|
? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA"))
|
||||||
: aa_text.toUpper());
|
: aa_text.toUpper());
|
||||||
|
@ -4901,6 +4908,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||||
|
|
||||||
UpdateUISettings();
|
UpdateUISettings();
|
||||||
game_list->SaveInterfaceLayout();
|
game_list->SaveInterfaceLayout();
|
||||||
|
UISettings::SaveWindowState();
|
||||||
hotkey_registry.SaveHotkeys();
|
hotkey_registry.SaveHotkeys();
|
||||||
|
|
||||||
// Unload controllers early
|
// Unload controllers early
|
||||||
|
@ -5055,9 +5063,9 @@ static void AdjustLinkColor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateUITheme() {
|
void GMainWindow::UpdateUITheme() {
|
||||||
const QString default_theme =
|
const QString default_theme = QString::fromUtf8(
|
||||||
QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second);
|
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
|
||||||
QString current_theme = UISettings::values.theme;
|
QString current_theme = QString::fromStdString(UISettings::values.theme);
|
||||||
|
|
||||||
if (current_theme.isEmpty()) {
|
if (current_theme.isEmpty()) {
|
||||||
current_theme = default_theme;
|
current_theme = default_theme;
|
||||||
|
@ -5085,7 +5093,7 @@ void GMainWindow::UpdateUITheme() {
|
||||||
QFile f(theme_uri);
|
QFile f(theme_uri);
|
||||||
if (!f.open(QFile::ReadOnly | QFile::Text)) {
|
if (!f.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
|
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
|
||||||
UISettings::values.theme.toStdString());
|
UISettings::values.theme);
|
||||||
current_theme = default_theme;
|
current_theme = default_theme;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5098,7 +5106,7 @@ void GMainWindow::UpdateUITheme() {
|
||||||
setStyleSheet(ts.readAll());
|
setStyleSheet(ts.readAll());
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
|
LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
|
||||||
UISettings::values.theme.toStdString());
|
UISettings::values.theme);
|
||||||
qApp->setStyleSheet({});
|
qApp->setStyleSheet({});
|
||||||
setStyleSheet({});
|
setStyleSheet({});
|
||||||
}
|
}
|
||||||
|
@ -5107,27 +5115,28 @@ void GMainWindow::UpdateUITheme() {
|
||||||
void GMainWindow::LoadTranslation() {
|
void GMainWindow::LoadTranslation() {
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
|
||||||
if (UISettings::values.language.isEmpty()) {
|
if (UISettings::values.language.empty()) {
|
||||||
// If the selected language is empty, use system locale
|
// If the selected language is empty, use system locale
|
||||||
loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
|
loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
|
||||||
} else {
|
} else {
|
||||||
// Otherwise load from the specified file
|
// Otherwise load from the specified file
|
||||||
loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/"));
|
loaded = translator.load(QString::fromStdString(UISettings::values.language),
|
||||||
|
QStringLiteral(":/languages/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
qApp->installTranslator(&translator);
|
qApp->installTranslator(&translator);
|
||||||
} else {
|
} else {
|
||||||
UISettings::values.language = QStringLiteral("en");
|
UISettings::values.language = std::string("en");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnLanguageChanged(const QString& locale) {
|
void GMainWindow::OnLanguageChanged(const QString& locale) {
|
||||||
if (UISettings::values.language != QStringLiteral("en")) {
|
if (UISettings::values.language != std::string("en")) {
|
||||||
qApp->removeTranslator(&translator);
|
qApp->removeTranslator(&translator);
|
||||||
}
|
}
|
||||||
|
|
||||||
UISettings::values.language = locale;
|
UISettings::values.language = locale.toStdString();
|
||||||
LoadTranslation();
|
LoadTranslation();
|
||||||
ui->retranslateUi(this);
|
ui->retranslateUi(this);
|
||||||
multiplayer_state->retranslateUi();
|
multiplayer_state->retranslateUi();
|
||||||
|
@ -5153,7 +5162,7 @@ void GMainWindow::changeEvent(QEvent* event) {
|
||||||
// UpdateUITheme is a decent work around
|
// UpdateUITheme is a decent work around
|
||||||
if (event->type() == QEvent::PaletteChange) {
|
if (event->type() == QEvent::PaletteChange) {
|
||||||
const QPalette test_palette(qApp->palette());
|
const QPalette test_palette(qApp->palette());
|
||||||
const QString current_theme = UISettings::values.theme;
|
const QString current_theme = QString::fromStdString(UISettings::values.theme);
|
||||||
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
|
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
|
||||||
static QColor last_window_color;
|
static QColor last_window_color;
|
||||||
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
|
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
|
||||||
|
@ -5247,7 +5256,8 @@ static void SetHighDPIAttributes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
std::unique_ptr<Config> config = std::make_unique<Config>();
|
std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>();
|
||||||
|
UISettings::RestoreWindowState(config);
|
||||||
bool has_broken_vulkan = false;
|
bool has_broken_vulkan = false;
|
||||||
bool is_child = false;
|
bool is_child = false;
|
||||||
if (CheckEnvVars(&is_child)) {
|
if (CheckEnvVars(&is_child)) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "configuration/qt_config.h"
|
||||||
#include "input_common/drivers/tas_input.h"
|
#include "input_common/drivers/tas_input.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
#include "yuzu/hotkeys.h"
|
#include "yuzu/hotkeys.h"
|
||||||
|
@ -26,7 +27,7 @@
|
||||||
#include <QtDBus/QtDBus>
|
#include <QtDBus/QtDBus>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Config;
|
class QtConfig;
|
||||||
class ClickableLabel;
|
class ClickableLabel;
|
||||||
class EmuThread;
|
class EmuThread;
|
||||||
class GameList;
|
class GameList;
|
||||||
|
@ -185,7 +186,7 @@ class GMainWindow : public QMainWindow {
|
||||||
public:
|
public:
|
||||||
void filterBarSetChecked(bool state);
|
void filterBarSetChecked(bool state);
|
||||||
void UpdateUITheme();
|
void UpdateUITheme();
|
||||||
explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan);
|
explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
|
||||||
~GMainWindow() override;
|
~GMainWindow() override;
|
||||||
|
|
||||||
bool DropAction(QDropEvent* event);
|
bool DropAction(QDropEvent* event);
|
||||||
|
@ -520,7 +521,7 @@ private:
|
||||||
QSlider* volume_slider = nullptr;
|
QSlider* volume_slider = nullptr;
|
||||||
QTimer status_bar_update_timer;
|
QTimer status_bar_update_timer;
|
||||||
|
|
||||||
std::unique_ptr<Config> config;
|
std::unique_ptr<QtConfig> config;
|
||||||
|
|
||||||
// Whether emulation is currently running in yuzu.
|
// Whether emulation is currently running in yuzu.
|
||||||
bool emulation_running = false;
|
bool emulation_running = false;
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
#include "yuzu/uisettings.h"
|
#include "yuzu/uisettings.h"
|
||||||
|
|
||||||
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
|
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
|
||||||
|
@ -15,6 +18,8 @@ template class Setting<unsigned long long>;
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace FS = Common::FS;
|
||||||
|
|
||||||
namespace UISettings {
|
namespace UISettings {
|
||||||
|
|
||||||
const Themes themes{{
|
const Themes themes{{
|
||||||
|
@ -28,10 +33,8 @@ const Themes themes{{
|
||||||
|
|
||||||
bool IsDarkTheme() {
|
bool IsDarkTheme() {
|
||||||
const auto& theme = UISettings::values.theme;
|
const auto& theme = UISettings::values.theme;
|
||||||
return theme == QStringLiteral("qdarkstyle") ||
|
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
|
||||||
theme == QStringLiteral("qdarkstyle_midnight_blue") ||
|
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
|
||||||
theme == QStringLiteral("colorful_dark") ||
|
|
||||||
theme == QStringLiteral("colorful_midnight_blue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Values values = {};
|
Values values = {};
|
||||||
|
@ -52,4 +55,58 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
|
||||||
return height * 16 / 9;
|
return height * 16 / 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaveWindowState() {
|
||||||
|
const auto window_state_config_loc =
|
||||||
|
FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
|
||||||
|
|
||||||
|
void(FS::CreateParentDir(window_state_config_loc));
|
||||||
|
QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
|
||||||
|
|
||||||
|
config.setValue(QStringLiteral("geometry"), values.geometry);
|
||||||
|
config.setValue(QStringLiteral("state"), values.state);
|
||||||
|
config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry);
|
||||||
|
config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state);
|
||||||
|
config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry);
|
||||||
|
|
||||||
|
config.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) {
|
||||||
|
const auto window_state_config_loc =
|
||||||
|
FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
|
||||||
|
|
||||||
|
// Migrate window state from old location
|
||||||
|
if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) {
|
||||||
|
const auto config_loc =
|
||||||
|
FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini");
|
||||||
|
QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat);
|
||||||
|
|
||||||
|
config.beginGroup(QStringLiteral("UI"));
|
||||||
|
config.beginGroup(QStringLiteral("UILayout"));
|
||||||
|
values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
|
||||||
|
values.state = config.value(QStringLiteral("state")).toByteArray();
|
||||||
|
values.renderwindow_geometry =
|
||||||
|
config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
|
||||||
|
values.gamelist_header_state =
|
||||||
|
config.value(QStringLiteral("gameListHeaderState")).toByteArray();
|
||||||
|
values.microprofile_geometry =
|
||||||
|
config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
|
||||||
|
config.endGroup();
|
||||||
|
config.endGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void(FS::CreateParentDir(window_state_config_loc));
|
||||||
|
const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
|
||||||
|
|
||||||
|
values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
|
||||||
|
values.state = config.value(QStringLiteral("state")).toByteArray();
|
||||||
|
values.renderwindow_geometry =
|
||||||
|
config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
|
||||||
|
values.gamelist_header_state =
|
||||||
|
config.value(QStringLiteral("gameListHeaderState")).toByteArray();
|
||||||
|
values.microprofile_geometry =
|
||||||
|
config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace UISettings
|
} // namespace UISettings
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
|
#include "configuration/qt_config.h"
|
||||||
|
|
||||||
using Settings::Category;
|
using Settings::Category;
|
||||||
using Settings::ConfirmStop;
|
using Settings::ConfirmStop;
|
||||||
|
@ -37,15 +38,15 @@ namespace UISettings {
|
||||||
bool IsDarkTheme();
|
bool IsDarkTheme();
|
||||||
|
|
||||||
struct ContextualShortcut {
|
struct ContextualShortcut {
|
||||||
QString keyseq;
|
std::string keyseq;
|
||||||
QString controller_keyseq;
|
std::string controller_keyseq;
|
||||||
int context;
|
int context;
|
||||||
bool repeat;
|
bool repeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Shortcut {
|
struct Shortcut {
|
||||||
QString name;
|
std::string name;
|
||||||
QString group;
|
std::string group;
|
||||||
ContextualShortcut shortcut;
|
ContextualShortcut shortcut;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,11 +59,19 @@ enum class Theme {
|
||||||
MidnightBlueColorful,
|
MidnightBlueColorful,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr Theme default_theme{
|
||||||
|
#ifdef _WIN32
|
||||||
|
Theme::DarkColorful
|
||||||
|
#else
|
||||||
|
Theme::DefaultColorful
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
using Themes = std::array<std::pair<const char*, const char*>, 6>;
|
using Themes = std::array<std::pair<const char*, const char*>, 6>;
|
||||||
extern const Themes themes;
|
extern const Themes themes;
|
||||||
|
|
||||||
struct GameDir {
|
struct GameDir {
|
||||||
QString path;
|
std::string path;
|
||||||
bool deep_scan = false;
|
bool deep_scan = false;
|
||||||
bool expanded = false;
|
bool expanded = false;
|
||||||
bool operator==(const GameDir& rhs) const {
|
bool operator==(const GameDir& rhs) const {
|
||||||
|
@ -144,15 +153,15 @@ struct Values {
|
||||||
Category::Screenshots};
|
Category::Screenshots};
|
||||||
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
|
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
|
||||||
|
|
||||||
QString roms_path;
|
std::string roms_path;
|
||||||
QString symbols_path;
|
std::string symbols_path;
|
||||||
QString game_dir_deprecated;
|
std::string game_dir_deprecated;
|
||||||
bool game_dir_deprecated_deepscan;
|
bool game_dir_deprecated_deepscan;
|
||||||
QVector<UISettings::GameDir> game_dirs;
|
QVector<GameDir> game_dirs;
|
||||||
QStringList recent_files;
|
QStringList recent_files;
|
||||||
QString language;
|
std::string language;
|
||||||
|
|
||||||
QString theme;
|
std::string theme;
|
||||||
|
|
||||||
// Shortcut name <Shortcut, context>
|
// Shortcut name <Shortcut, context>
|
||||||
std::vector<Shortcut> shortcuts;
|
std::vector<Shortcut> shortcuts;
|
||||||
|
@ -206,6 +215,54 @@ extern Values values;
|
||||||
|
|
||||||
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
|
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
|
||||||
|
|
||||||
|
void SaveWindowState();
|
||||||
|
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
|
||||||
|
|
||||||
|
// This shouldn't have anything except static initializers (no functions). So
|
||||||
|
// QKeySequence(...).toString() is NOT ALLOWED HERE.
|
||||||
|
// 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<Shortcut, 23> default_hotkeys{{
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
|
||||||
|
}};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
} // namespace UISettings
|
} // namespace UISettings
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(UISettings::GameDir*);
|
Q_DECLARE_METATYPE(UISettings::GameDir*);
|
||||||
|
|
||||||
|
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
|
||||||
|
Q_DECLARE_METATYPE(Settings::CpuAccuracy);
|
||||||
|
Q_DECLARE_METATYPE(Settings::GpuAccuracy);
|
||||||
|
Q_DECLARE_METATYPE(Settings::FullscreenMode);
|
||||||
|
Q_DECLARE_METATYPE(Settings::NvdecEmulation);
|
||||||
|
Q_DECLARE_METATYPE(Settings::ResolutionSetup);
|
||||||
|
Q_DECLARE_METATYPE(Settings::ScalingFilter);
|
||||||
|
Q_DECLARE_METATYPE(Settings::AntiAliasing);
|
||||||
|
Q_DECLARE_METATYPE(Settings::RendererBackend);
|
||||||
|
Q_DECLARE_METATYPE(Settings::ShaderBackend);
|
||||||
|
Q_DECLARE_METATYPE(Settings::AstcRecompression);
|
||||||
|
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
|
||||||
|
|
|
@ -13,9 +13,6 @@ function(create_resource file output filename)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
add_executable(yuzu-cmd
|
add_executable(yuzu-cmd
|
||||||
config.cpp
|
|
||||||
config.h
|
|
||||||
default_ini.h
|
|
||||||
emu_window/emu_window_sdl2.cpp
|
emu_window/emu_window_sdl2.cpp
|
||||||
emu_window/emu_window_sdl2.h
|
emu_window/emu_window_sdl2.h
|
||||||
emu_window/emu_window_sdl2_gl.cpp
|
emu_window/emu_window_sdl2_gl.cpp
|
||||||
|
@ -25,14 +22,16 @@ add_executable(yuzu-cmd
|
||||||
emu_window/emu_window_sdl2_vk.cpp
|
emu_window/emu_window_sdl2_vk.cpp
|
||||||
emu_window/emu_window_sdl2_vk.h
|
emu_window/emu_window_sdl2_vk.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
|
sdl_config.cpp
|
||||||
|
sdl_config.h
|
||||||
yuzu.cpp
|
yuzu.cpp
|
||||||
yuzu.rc
|
yuzu.rc
|
||||||
)
|
)
|
||||||
|
|
||||||
create_target_directory_groups(yuzu-cmd)
|
create_target_directory_groups(yuzu-cmd)
|
||||||
|
|
||||||
target_link_libraries(yuzu-cmd PRIVATE common core input_common)
|
target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common)
|
||||||
target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad)
|
target_link_libraries(yuzu-cmd PRIVATE glad)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_libraries(yuzu-cmd PRIVATE getopt)
|
target_link_libraries(yuzu-cmd PRIVATE getopt)
|
||||||
endif()
|
endif()
|
||||||
|
|
257
src/yuzu_cmd/sdl_config.cpp
Executable file
257
src/yuzu_cmd/sdl_config.cpp
Executable file
|
@ -0,0 +1,257 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h
|
||||||
|
#define SDL_MAIN_HANDLED
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include "input_common/main.h"
|
||||||
|
#include "sdl_config.h"
|
||||||
|
|
||||||
|
const std::array<int, Settings::NativeButton::NumButtons> SdlConfig::default_buttons = {
|
||||||
|
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
|
||||||
|
SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
|
||||||
|
SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<int, Settings::NativeMotion::NumMotions> SdlConfig::default_motions = {
|
||||||
|
SDL_SCANCODE_7,
|
||||||
|
SDL_SCANCODE_8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
SDL_SCANCODE_UP,
|
||||||
|
SDL_SCANCODE_DOWN,
|
||||||
|
SDL_SCANCODE_LEFT,
|
||||||
|
SDL_SCANCODE_RIGHT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SDL_SCANCODE_I,
|
||||||
|
SDL_SCANCODE_K,
|
||||||
|
SDL_SCANCODE_J,
|
||||||
|
SDL_SCANCODE_L,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const std::array<int, 2> SdlConfig::default_stick_mod = {
|
||||||
|
SDL_SCANCODE_D,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<int, 2> SdlConfig::default_ringcon_analogs{{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
}};
|
||||||
|
|
||||||
|
SdlConfig::SdlConfig(const std::optional<std::string> config_path) {
|
||||||
|
Initialize(config_path);
|
||||||
|
ReadSdlValues();
|
||||||
|
SaveSdlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
SdlConfig::~SdlConfig() {
|
||||||
|
if (global) {
|
||||||
|
SdlConfig::SaveAllValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReloadAllValues() {
|
||||||
|
Reload();
|
||||||
|
ReadSdlValues();
|
||||||
|
SaveSdlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveAllValues() {
|
||||||
|
Save();
|
||||||
|
SaveSdlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadSdlValues() {
|
||||||
|
ReadSdlControlValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadSdlControlValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
Settings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||||
|
ReadSdlPlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ReadDebugControlValues();
|
||||||
|
ReadHidbusValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix.append("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
const auto profile_name =
|
||||||
|
ReadStringSetting(std::string(player_prefix).append("profile_name"));
|
||||||
|
if (profile_name.empty()) {
|
||||||
|
// Use the global input config
|
||||||
|
player = Settings::values.players.GetValue(true)[player_index];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
auto& player_buttons = player.buttons[i];
|
||||||
|
|
||||||
|
player_buttons = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
|
||||||
|
if (player_buttons.empty()) {
|
||||||
|
player_buttons = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
auto& player_analogs = player.analogs[i];
|
||||||
|
|
||||||
|
player_analogs = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
|
||||||
|
if (player_analogs.empty()) {
|
||||||
|
player_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||||
|
auto& player_motions = player.motions[i];
|
||||||
|
|
||||||
|
player_motions = ReadStringSetting(
|
||||||
|
std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
|
||||||
|
if (player_motions.empty()) {
|
||||||
|
player_motions = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadDebugControlValues() {
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
|
||||||
|
debug_pad_buttons = ReadStringSetting(
|
||||||
|
std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
|
||||||
|
if (debug_pad_buttons.empty()) {
|
||||||
|
debug_pad_buttons = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
|
||||||
|
debug_pad_analogs = ReadStringSetting(
|
||||||
|
std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
|
||||||
|
if (debug_pad_analogs.empty()) {
|
||||||
|
debug_pad_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::ReadHidbusValues() {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
|
||||||
|
auto& ringcon_analogs = Settings::values.ringcon_analogs;
|
||||||
|
|
||||||
|
ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
|
||||||
|
if (ringcon_analogs.empty()) {
|
||||||
|
ringcon_analogs = default_param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveSdlValues() {
|
||||||
|
SaveSdlControlValues();
|
||||||
|
|
||||||
|
WriteToIni();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveSdlControlValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
|
||||||
|
|
||||||
|
Settings::values.players.SetGlobal(!IsCustomConfig());
|
||||||
|
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||||
|
SaveSdlPlayerValues(p);
|
||||||
|
}
|
||||||
|
if (IsCustomConfig()) {
|
||||||
|
EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveDebugControlValues();
|
||||||
|
SaveHidbusValues();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
|
||||||
|
std::string player_prefix;
|
||||||
|
if (type != ConfigType::InputProfile) {
|
||||||
|
player_prefix = std::string("player_").append(ToString(player_index)).append("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
if (IsCustomConfig() && player.profile_name.empty()) {
|
||||||
|
// No custom profile selected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
|
||||||
|
player.buttons[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
|
||||||
|
player.analogs[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||||
|
WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
|
||||||
|
player.motions[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveDebugControlValues() {
|
||||||
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
|
WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
|
||||||
|
Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||||
|
default_analogs[i][3], default_stick_mod[i], 0.5f);
|
||||||
|
WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
|
||||||
|
Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlConfig::SaveHidbusValues() {
|
||||||
|
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||||
|
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
|
||||||
|
WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
|
||||||
|
std::make_optional(default_param));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {
|
||||||
|
return Settings::values.linkage.by_category[category];
|
||||||
|
}
|
49
src/yuzu_cmd/sdl_config.h
Executable file
49
src/yuzu_cmd/sdl_config.h
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "frontend_common/config.h"
|
||||||
|
|
||||||
|
class SdlConfig final : public Config {
|
||||||
|
public:
|
||||||
|
explicit SdlConfig(std::optional<std::string> config_path);
|
||||||
|
~SdlConfig() override;
|
||||||
|
|
||||||
|
void ReloadAllValues() override;
|
||||||
|
void SaveAllValues() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadSdlValues();
|
||||||
|
void ReadSdlPlayerValues(std::size_t player_index);
|
||||||
|
void ReadSdlControlValues();
|
||||||
|
void ReadHidbusValues() override;
|
||||||
|
void ReadDebugControlValues() override;
|
||||||
|
void ReadPathValues() override {}
|
||||||
|
void ReadShortcutValues() override {}
|
||||||
|
void ReadUIValues() override {}
|
||||||
|
void ReadUIGamelistValues() override {}
|
||||||
|
void ReadUILayoutValues() override {}
|
||||||
|
void ReadMultiplayerValues() override {}
|
||||||
|
|
||||||
|
void SaveSdlValues();
|
||||||
|
void SaveSdlPlayerValues(std::size_t player_index);
|
||||||
|
void SaveSdlControlValues();
|
||||||
|
void SaveHidbusValues() override;
|
||||||
|
void SaveDebugControlValues() override;
|
||||||
|
void SavePathValues() override {}
|
||||||
|
void SaveShortcutValues() override {}
|
||||||
|
void SaveUIValues() override {}
|
||||||
|
void SaveUIGamelistValues() override {}
|
||||||
|
void SaveUILayoutValues() override {}
|
||||||
|
void SaveMultiplayerValues() override {}
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||||
|
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
|
||||||
|
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||||
|
static const std::array<int, 2> default_stick_mod;
|
||||||
|
static const std::array<int, 2> default_ringcon_analogs;
|
||||||
|
};
|
|
@ -29,10 +29,11 @@
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/telemetry_session.h"
|
#include "core/telemetry_session.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
|
#include "sdl_config.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "yuzu_cmd/config.h"
|
|
||||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
|
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
|
||||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
|
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
|
||||||
|
@ -300,7 +301,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Config config{config_path};
|
SdlConfig config{config_path};
|
||||||
|
|
||||||
// apply the log_filter setting
|
// apply the log_filter setting
|
||||||
// the logger was initialized before and doesn't pick up the filter on its own
|
// the logger was initialized before and doesn't pick up the filter on its own
|
||||||
|
|
Loading…
Reference in a new issue