mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-04 23:55:08 +01:00
Rework how settings are shared between Kotlin and native side
Settings are now shared to the native side by passing an instance of the Kotlin's `Settings` class. This way the C++ `Settings` class doesn't need to parse the SharedPreferences xml anymore.
This commit is contained in:
parent
4be8b4cf66
commit
c5dde5953a
@ -3,7 +3,6 @@
|
||||
|
||||
#include <csignal>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include "skyline/common.h"
|
||||
@ -28,6 +27,7 @@ std::weak_ptr<skyline::kernel::OS> OsWeak;
|
||||
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
|
||||
std::weak_ptr<skyline::audio::Audio> AudioWeak;
|
||||
std::weak_ptr<skyline::input::Input> InputWeak;
|
||||
std::weak_ptr<skyline::Settings> SettingsWeak;
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
|
||||
static std::string GetTimeZoneName() {
|
||||
@ -54,15 +54,23 @@ static std::string GetTimeZoneName() {
|
||||
return "GMT";
|
||||
}
|
||||
|
||||
template<> void skyline::Settings::Update<skyline::KtSettings>(KtSettings newSettings) {
|
||||
operationMode = newSettings.GetBool("operationMode");
|
||||
usernameValue = newSettings.GetString("usernameValue");
|
||||
systemLanguage = newSettings.GetInt<skyline::language::SystemLanguage>("systemLanguage");
|
||||
forceTripleBuffering = newSettings.GetBool("forceTripleBuffering");
|
||||
disableFrameThrottling = newSettings.GetBool("disableFrameThrottling");
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_SkylineApplication_initializeLog(
|
||||
JNIEnv *env,
|
||||
jobject,
|
||||
jstring appFilesPathJstring,
|
||||
jstring publicAppFilesPathJstring,
|
||||
jint logLevel
|
||||
) {
|
||||
std::string appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)};
|
||||
skyline::JniString publicAppFilesPath(env, publicAppFilesPathJstring);
|
||||
skyline::Logger::configLevel = static_cast<skyline::Logger::LogLevel>(logLevel);
|
||||
skyline::Logger::LoaderContext.Initialize(appFilesPath + "logs/loader.sklog");
|
||||
skyline::Logger::LoaderContext.Initialize(publicAppFilesPath + "logs/loader.sklog");
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
@ -71,8 +79,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
jstring romUriJstring,
|
||||
jint romType,
|
||||
jint romFd,
|
||||
jint preferenceFd,
|
||||
jint systemLanguage,
|
||||
jobject settingsInstance,
|
||||
jstring publicAppFilesPathJstring,
|
||||
jstring privateAppFilesPathJstring,
|
||||
jstring nativeLibraryPathJstring,
|
||||
@ -85,8 +92,9 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
pthread_setname_np(pthread_self(), "EmuMain");
|
||||
|
||||
auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)};
|
||||
auto settings{std::make_shared<skyline::Settings>(preferenceFd)};
|
||||
close(preferenceFd);
|
||||
|
||||
skyline::KtSettings ktSettings{env, settingsInstance};
|
||||
auto settings{std::make_shared<skyline::Settings>(ktSettings)};
|
||||
|
||||
skyline::JniString publicAppFilesPath(env, publicAppFilesPathJstring);
|
||||
skyline::Logger::EmulationContext.Initialize(publicAppFilesPath + "logs/emulation.sklog");
|
||||
@ -110,13 +118,13 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
privateAppFilesPath,
|
||||
nativeLibraryPath,
|
||||
GetTimeZoneName(),
|
||||
static_cast<skyline::language::SystemLanguage>(systemLanguage),
|
||||
std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager))
|
||||
)};
|
||||
OsWeak = os;
|
||||
GpuWeak = os->state.gpu;
|
||||
AudioWeak = os->state.audio;
|
||||
InputWeak = os->state.input;
|
||||
SettingsWeak = settings;
|
||||
jvmManager->InitializeControllers();
|
||||
|
||||
skyline::Logger::InfoNoPrefix("Launching ROM {}", skyline::JniString(env, romUriJstring));
|
||||
|
@ -1,56 +1,12 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#include "settings.h"
|
||||
|
||||
namespace skyline {
|
||||
Settings::Settings(int fd) {
|
||||
pugi::xml_document document;
|
||||
auto result{document.load_file(fmt::format("/proc/self/fd/{}", fd).c_str())};
|
||||
if (!result)
|
||||
throw exception("PugiXML Error: {} at {}", result.description(), result.offset);
|
||||
|
||||
#define PREF_ELEM(name, memberName, rhs) std::make_pair(std::string(name), [](Settings &settings, const pugi::xml_node &element) { settings.memberName = rhs; })
|
||||
|
||||
std::tuple preferences{
|
||||
PREF_ELEM("log_level", logLevel, static_cast<Logger::LogLevel>(element.text().as_uint(static_cast<unsigned int>(Logger::LogLevel::Info)))),
|
||||
PREF_ELEM("username_value", username, element.text().as_string()),
|
||||
PREF_ELEM("operation_mode", operationMode, element.attribute("value").as_bool()),
|
||||
PREF_ELEM("force_triple_buffering", forceTripleBuffering, element.attribute("value").as_bool()),
|
||||
PREF_ELEM("disable_frame_throttling", disableFrameThrottling, element.attribute("value").as_bool()),
|
||||
};
|
||||
|
||||
#undef PREF_ELEM
|
||||
|
||||
std::bitset<std::tuple_size_v<decltype(preferences)>> preferencesSet{}; // A bitfield to keep track of all the preferences we've set
|
||||
for (auto element{document.last_child().first_child()}; element; element = element.next_sibling()) {
|
||||
std::string_view name{element.attribute("name").value()};
|
||||
std::apply([&](auto... preferences) {
|
||||
size_t index{};
|
||||
([&](auto preference) {
|
||||
if (name.size() == preference.first.size() && name.starts_with(preference.first)) {
|
||||
preference.second(*this, element);
|
||||
preferencesSet.set(index);
|
||||
}
|
||||
index++;
|
||||
}(preferences), ...);
|
||||
}, preferences);
|
||||
}
|
||||
|
||||
if (!preferencesSet.all()) {
|
||||
std::string unsetPreferences;
|
||||
std::apply([&](auto... preferences) {
|
||||
size_t index{};
|
||||
([&](auto preference) {
|
||||
if (!preferencesSet.test(index))
|
||||
unsetPreferences += std::string("\n* ") + preference.first;
|
||||
index++;
|
||||
}(preferences), ...);
|
||||
}, preferences);
|
||||
throw exception("Cannot find the following preferences:{}", unsetPreferences);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @note This is a placeholder implementation, it must be overridden via template specialisation for platform-specific behavior
|
||||
*/
|
||||
template<class T>
|
||||
void Settings::Update(T newSettings) {}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include "language.h"
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
@ -11,15 +11,25 @@ namespace skyline {
|
||||
*/
|
||||
class Settings {
|
||||
public:
|
||||
Logger::LogLevel logLevel; //!< The minimum level that logs need to be for them to be printed
|
||||
std::string username; //!< The name set by the user to be supplied to the guest
|
||||
// System
|
||||
bool operationMode; //!< If the emulated Switch should be handheld or docked
|
||||
std::string usernameValue; //!< The name set by the user to be supplied to the guest
|
||||
language::SystemLanguage systemLanguage; //!< The system language set by the user
|
||||
|
||||
// Display
|
||||
bool forceTripleBuffering; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering
|
||||
bool disableFrameThrottling; //!< Allow the guest to submit frames without any blocking calls
|
||||
|
||||
template<class T>
|
||||
Settings(T settings) {
|
||||
Update(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fd An FD to the preference XML file
|
||||
* @brief Updates settings with the given values
|
||||
* @param newSettings A platform-specific object containing the new settings' values
|
||||
*/
|
||||
Settings(int fd);
|
||||
template<class T>
|
||||
void Update(T newSettings);
|
||||
};
|
||||
}
|
||||
|
@ -18,6 +18,43 @@ namespace skyline {
|
||||
JniString(JNIEnv *env, jstring jString) : std::string(GetJString(env, jString)) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A wrapper over the `Settings` Kotlin class
|
||||
* @note The lifetime of this class must not exceed that of the JNI environment
|
||||
*/
|
||||
class KtSettings {
|
||||
private:
|
||||
JNIEnv *env; //!< A pointer to the current jni environment
|
||||
jclass settingsClass; //!< The settings class
|
||||
jobject settingsInstance; //!< The settings instance
|
||||
|
||||
public:
|
||||
KtSettings(JNIEnv *env, jobject settingsInstance) : env(env), settingsInstance(settingsInstance), settingsClass(env->GetObjectClass(settingsInstance)) {}
|
||||
|
||||
/**
|
||||
* @param key A null terminated string containing the key of the setting to get
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::is_integral_v<T> || std::is_enum_v<T>
|
||||
T GetInt(const std::string_view &key) {
|
||||
return static_cast<T>(env->GetIntField(settingsInstance, env->GetFieldID(settingsClass, key.data(), "I")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key A null terminated string containing the key of the setting to get
|
||||
*/
|
||||
bool GetBool(const std::string_view &key) {
|
||||
return env->GetBooleanField(settingsInstance, env->GetFieldID(settingsClass, key.data(), "Z")) == JNI_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key A null terminated string containing the key of the setting to get
|
||||
*/
|
||||
JniString GetString(const std::string_view &key) {
|
||||
return {env, static_cast<jstring>(env->GetObjectField(settingsInstance, env->GetFieldID(settingsClass, key.data(), "Ljava/lang/String;")))};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The JvmManager class is used to simplify transactions with the Java component
|
||||
*/
|
||||
|
@ -20,7 +20,6 @@ namespace skyline::kernel {
|
||||
std::string privateAppFilesPath,
|
||||
std::string nativeLibraryPath,
|
||||
std::string deviceTimeZone,
|
||||
language::SystemLanguage systemLanguage,
|
||||
std::shared_ptr<vfs::FileSystem> assetFileSystem)
|
||||
: nativeLibraryPath(std::move(nativeLibraryPath)),
|
||||
publicAppFilesPath(std::move(publicAppFilesPath)),
|
||||
@ -28,8 +27,7 @@ namespace skyline::kernel {
|
||||
state(this, jvmManager, settings),
|
||||
deviceTimeZone(std::move(deviceTimeZone)),
|
||||
assetFileSystem(std::move(assetFileSystem)),
|
||||
serviceManager(state),
|
||||
systemLanguage(systemLanguage) {}
|
||||
serviceManager(state) {}
|
||||
|
||||
void OS::Execute(int romFd, loader::RomFormat romType) {
|
||||
auto romFile{std::make_shared<vfs::OsBacking>(romFd)};
|
||||
|
@ -21,7 +21,6 @@ namespace skyline::kernel {
|
||||
std::string deviceTimeZone; //!< The timezone name (e.g. Europe/London)
|
||||
std::shared_ptr<vfs::FileSystem> assetFileSystem; //!< A filesystem to be used for accessing emulator assets (like tzdata)
|
||||
service::ServiceManager serviceManager;
|
||||
language::SystemLanguage systemLanguage;
|
||||
|
||||
/**
|
||||
* @param settings An instance of the Settings class
|
||||
@ -34,7 +33,6 @@ namespace skyline::kernel {
|
||||
std::string privateAppFilesPath,
|
||||
std::string deviceTimeZone,
|
||||
std::string nativeLibraryPath,
|
||||
language::SystemLanguage systemLanguage,
|
||||
std::shared_ptr<vfs::FileSystem> assetFileSystem
|
||||
);
|
||||
|
||||
|
@ -31,8 +31,8 @@ namespace skyline::service::account {
|
||||
.uid = userId,
|
||||
};
|
||||
|
||||
size_t usernameSize{std::min(accountProfileBase.nickname.size() - 1, state.settings->username.size())};
|
||||
std::memcpy(accountProfileBase.nickname.data(), state.settings->username.c_str(), usernameSize);
|
||||
size_t usernameSize{std::min(accountProfileBase.nickname.size() - 1, state.settings->usernameValue.size())};
|
||||
std::memcpy(accountProfileBase.nickname.data(), state.settings->usernameValue.c_str(), usernameSize);
|
||||
|
||||
response.Push(accountProfileBase);
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <common/uuid.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <loader/loader.h>
|
||||
#include <os.h>
|
||||
#include <common/settings.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/account/IAccountServiceForApplication.h>
|
||||
#include <services/am/storage/VectorIStorage.h>
|
||||
@ -58,9 +58,9 @@ namespace skyline::service::am {
|
||||
}
|
||||
|
||||
Result IApplicationFunctions::GetDesiredLanguage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto desiredLanguage{language::GetApplicationLanguage(state.os->systemLanguage)};
|
||||
auto desiredLanguage{language::GetApplicationLanguage(state.settings->systemLanguage)};
|
||||
|
||||
// In the future we might want to trigger an UI dialog if the user selected languages is not available, for now it will use the first available
|
||||
// In the future we might want to trigger an UI dialog if the user-selected language is not available, for now it will use the first one available
|
||||
if (((1 << static_cast<u32>(desiredLanguage)) & state.loader->nacp->nacpContents.supportedLanguageFlag) == 0)
|
||||
desiredLanguage = state.loader->nacp->GetFirstSupportedLanguage();
|
||||
|
||||
|
@ -30,7 +30,7 @@ import emu.skyline.input.*
|
||||
import emu.skyline.loader.getRomFormat
|
||||
import emu.skyline.utils.ByteBufferSerializable
|
||||
import emu.skyline.utils.Settings
|
||||
import java.io.File
|
||||
import emu.skyline.utils.SettingsValues
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.util.concurrent.FutureTask
|
||||
@ -85,13 +85,13 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
* @param romUri The URI of the ROM as a string, used to print out in the logs
|
||||
* @param romType The type of the ROM as an enum value
|
||||
* @param romFd The file descriptor of the ROM object
|
||||
* @param preferenceFd The file descriptor of the Preference XML
|
||||
* @param settingsValues The SettingsValues instance
|
||||
* @param publicAppFilesPath The full path to the public app files directory
|
||||
* @param privateAppFilesPath The full path to the private app files directory
|
||||
* @param nativeLibraryPath The full path to the app native library directory
|
||||
* @param assetManager The asset manager used for accessing app assets
|
||||
*/
|
||||
private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, language : Int, publicAppFilesPath : String, privateAppFilesPath : String, nativeLibraryPath : String, assetManager : AssetManager)
|
||||
private external fun executeApplication(romUri : String, romType : Int, romFd : Int, settingsValues : SettingsValues, publicAppFilesPath : String, privateAppFilesPath : String, nativeLibraryPath : String, assetManager : AssetManager)
|
||||
|
||||
/**
|
||||
* @param join If the function should only return after all the threads join or immediately
|
||||
@ -247,10 +247,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
val rom = intent.data!!
|
||||
val romType = getRomFormat(rom, contentResolver).ordinal
|
||||
val romFd = contentResolver.openFileDescriptor(rom, "r")!!
|
||||
val preferenceFd = ParcelFileDescriptor.open(File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml"), ParcelFileDescriptor.MODE_READ_WRITE)
|
||||
|
||||
emulationThread = Thread {
|
||||
executeApplication(rom.toString(), romType, romFd.detachFd(), preferenceFd.detachFd(), settings.systemLanguage, applicationContext.getPublicFilesDir().canonicalPath + "/", applicationContext.filesDir.canonicalPath + "/", applicationInfo.nativeLibraryDir + "/", assets)
|
||||
executeApplication(rom.toString(), romType, romFd.detachFd(), SettingsValues(settings), applicationContext.getPublicFilesDir().canonicalPath + "/", applicationContext.filesDir.canonicalPath + "/", applicationInfo.nativeLibraryDir + "/", assets)
|
||||
returnFromEmulation()
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ class SkylineApplication : Application() {
|
||||
instance = this
|
||||
System.loadLibrary("skyline")
|
||||
|
||||
val appFilesPath = applicationContext.getPublicFilesDir().canonicalPath
|
||||
File("$appFilesPath/logs/").mkdirs()
|
||||
initializeLog("$appFilesPath/", getSettings().logLevel)
|
||||
val publicAppFilesPath = applicationContext.getPublicFilesDir().canonicalPath
|
||||
File("$publicAppFilesPath/logs/").mkdirs()
|
||||
initializeLog("$publicAppFilesPath/", getSettings().logLevel)
|
||||
}
|
||||
}
|
||||
|
16
app/src/main/java/emu/skyline/utils/SettingsValues.kt
Normal file
16
app/src/main/java/emu/skyline/utils/SettingsValues.kt
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.utils
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
class SettingsValues(pref: Settings) : Serializable {
|
||||
var isDocked : Boolean = pref.isDocked
|
||||
var usernameValue : String = pref.usernameValue
|
||||
var systemLanguage : Int = pref.systemLanguage
|
||||
var forceTripleBuffering : Boolean = pref.forceTripleBuffering
|
||||
var disableFrameThrottling : Boolean = pref.disableFrameThrottling
|
||||
}
|
Loading…
Reference in New Issue
Block a user