Make every setting observable individually

A `Setting` delegate class has been introduced, holding the raw value of the setting and adding support for registering callbacks to that setting. Callbacks will then be called when the value of that setting changes.
As a result of this, raw setting values have been made accessible through pointer dereference semantics.
This commit is contained in:
lynxnb 2022-07-22 10:53:40 +02:00 committed by ◱ Mark
parent 2d70be60d1
commit 3905728447
7 changed files with 51 additions and 37 deletions

View File

@ -60,8 +60,6 @@ template<> void skyline::Settings::Update<skyline::KtSettings>(KtSettings newSet
systemLanguage = newSettings.GetInt<skyline::language::SystemLanguage>("systemLanguage"); systemLanguage = newSettings.GetInt<skyline::language::SystemLanguage>("systemLanguage");
forceTripleBuffering = newSettings.GetBool("forceTripleBuffering"); forceTripleBuffering = newSettings.GetBool("forceTripleBuffering");
disableFrameThrottling = newSettings.GetBool("disableFrameThrottling"); disableFrameThrottling = newSettings.GetBool("disableFrameThrottling");
OnSettingsChanged();
} }
extern "C" JNIEXPORT void Java_emu_skyline_SkylineApplication_initializeLog( extern "C" JNIEXPORT void Java_emu_skyline_SkylineApplication_initializeLog(

View File

@ -4,16 +4,6 @@
#include "settings.h" #include "settings.h"
namespace skyline { namespace skyline {
void Settings::Subscribe(Callback callback) {
callbacks.push_back(std::move(callback));
}
void Settings::OnSettingsChanged() {
std::for_each(callbacks.begin(), callbacks.end(), [&](const Callback& listener) {
listener(*this);
});
}
/** /**
* @note This is a placeholder implementation, it must be overridden via template specialisation for platform-specific behavior * @note This is a placeholder implementation, it must be overridden via template specialisation for platform-specific behavior
*/ */

View File

@ -10,15 +10,55 @@ namespace skyline {
* @brief The Settings class is used to access preferences set in the Kotlin component of Skyline * @brief The Settings class is used to access preferences set in the Kotlin component of Skyline
*/ */
class Settings { class Settings {
template<typename T>
class Setting {
using Callback = std::function<void(const T &)>;
std::vector<Callback> callbacks; //!< Callbacks to be called when this setting changes
T value;
/**
* @brief Calls all callbacks registered for this setting
*/
void OnSettingChanged() {
for (const auto &callback : callbacks)
callback(value);
}
public:
/**
* @return The underlying setting value
*/
const T &operator*() const {
return value;
}
/**
* @brief Sets the underlying setting value, signalling any callbacks if necessary
*/
void operator=(T newValue) {
if (value != newValue) {
value = std::move(newValue);
OnSettingChanged();
}
}
/**
* @brief Register a callback to be run when this setting changes
*/
void AddCallback(Callback callback) {
callbacks.push_back(std::move(callback));
}
};
public: public:
// System // System
bool isDocked; //!< If the emulated Switch should be handheld or docked Setting<bool> isDocked; //!< If the emulated Switch should be handheld or docked
std::string usernameValue; //!< The name set by the user to be supplied to the guest Setting<std::string> usernameValue; //!< The user name to be supplied to the guest
language::SystemLanguage systemLanguage; //!< The system language set by the user Setting<language::SystemLanguage> systemLanguage; //!< The system language
// Display // Display
bool forceTripleBuffering; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering Setting<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 Setting<bool> disableFrameThrottling; //!< Allow the guest to submit frames without any blocking calls
template<class T> template<class T>
Settings(T settings) { Settings(T settings) {
@ -27,23 +67,9 @@ namespace skyline {
/** /**
* @brief Updates settings with the given values * @brief Updates settings with the given values
* @note The implementations of this method must call OnSettingsChanged
* @param newSettings A platform-specific object containing the new settings' values * @param newSettings A platform-specific object containing the new settings' values
*/ */
template<class T> template<class T>
void Update(T newSettings); void Update(T newSettings);
using Callback = std::function<void(const Settings &)>;
/**
* @brief Subscribe to settings changes
* @param callback The function to be called when settings change
*/
void Subscribe(Callback callback);
private:
std::vector<Callback> callbacks; //!< Callbacks to be called when settings change
void OnSettingsChanged();
}; };
} }

View File

@ -110,7 +110,7 @@ namespace skyline::gpu {
} }
void PresentationEngine::UpdateSwapchain(texture::Format format, texture::Dimensions extent) { void PresentationEngine::UpdateSwapchain(texture::Format format, texture::Dimensions extent) {
auto minImageCount{std::max(vkSurfaceCapabilities.minImageCount, state.settings->forceTripleBuffering ? 3U : 2U)}; auto minImageCount{std::max(vkSurfaceCapabilities.minImageCount, *state.settings->forceTripleBuffering ? 3U : 2U)};
if (minImageCount > MaxSwapchainImageCount) if (minImageCount > MaxSwapchainImageCount)
throw exception("Requesting swapchain with higher image count ({}) than maximum slot count ({})", minImageCount, MaxSwapchainImageCount); throw exception("Requesting swapchain with higher image count ({}) than maximum slot count ({})", minImageCount, MaxSwapchainImageCount);
@ -131,7 +131,7 @@ namespace skyline::gpu {
if ((capabilities.supportedUsageFlags & presentUsage) != presentUsage) if ((capabilities.supportedUsageFlags & presentUsage) != presentUsage)
throw exception("Swapchain doesn't support image usage '{}': {}", vk::to_string(presentUsage), vk::to_string(capabilities.supportedUsageFlags)); throw exception("Swapchain doesn't support image usage '{}': {}", vk::to_string(presentUsage), vk::to_string(capabilities.supportedUsageFlags));
auto requestedMode{state.settings->disableFrameThrottling ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo}; auto requestedMode{*state.settings->disableFrameThrottling ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo};
auto modes{gpu.vkPhysicalDevice.getSurfacePresentModesKHR(**vkSurface)}; auto modes{gpu.vkPhysicalDevice.getSurfacePresentModesKHR(**vkSurface)};
if (std::find(modes.begin(), modes.end(), requestedMode) == modes.end()) if (std::find(modes.begin(), modes.end(), requestedMode) == modes.end())
throw exception("Swapchain doesn't support present mode: {}", vk::to_string(requestedMode)); throw exception("Swapchain doesn't support present mode: {}", vk::to_string(requestedMode));

View File

@ -31,8 +31,8 @@ namespace skyline::service::account {
.uid = userId, .uid = userId,
}; };
size_t usernameSize{std::min(accountProfileBase.nickname.size() - 1, state.settings->usernameValue.size())}; size_t usernameSize{std::min(accountProfileBase.nickname.size() - 1, (*state.settings->usernameValue).size())};
std::memcpy(accountProfileBase.nickname.data(), state.settings->usernameValue.c_str(), usernameSize); std::memcpy(accountProfileBase.nickname.data(), (*state.settings->usernameValue).c_str(), usernameSize);
response.Push(accountProfileBase); response.Push(accountProfileBase);

View File

@ -58,7 +58,7 @@ namespace skyline::service::am {
} }
Result IApplicationFunctions::GetDesiredLanguage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationFunctions::GetDesiredLanguage(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto desiredLanguage{language::GetApplicationLanguage(state.settings->systemLanguage)}; auto desiredLanguage{language::GetApplicationLanguage(*state.settings->systemLanguage)};
// 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 // 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) if (((1 << static_cast<u32>(desiredLanguage)) & state.loader->nacp->nacpContents.supportedLanguageFlag) == 0)

View File

@ -15,7 +15,7 @@ namespace skyline::service::am {
: BaseService(state, manager), : BaseService(state, manager),
messageEvent(std::make_shared<type::KEvent>(state, false)), messageEvent(std::make_shared<type::KEvent>(state, false)),
defaultDisplayResolutionChangeEvent(std::make_shared<type::KEvent>(state, false)) { defaultDisplayResolutionChangeEvent(std::make_shared<type::KEvent>(state, false)) {
operationMode = static_cast<OperationMode>(state.settings->isDocked); operationMode = static_cast<OperationMode>(*state.settings->isDocked);
Logger::Info("Switch to mode: {}", static_cast<bool>(operationMode) ? "Docked" : "Handheld"); Logger::Info("Switch to mode: {}", static_cast<bool>(operationMode) ? "Docked" : "Handheld");
QueueMessage(Message::FocusStateChange); QueueMessage(Message::FocusStateChange);
} }