Sunshine/src/platform/windows/nvprefs/nvprefs_interface.cpp
ns6089 f245f777f7 Modify and restore NVIDIA control panel settings
Namely:
- Power profile for sunshine.exe
- Global OpenGL/Vulkan DXGI swapchain
2023-07-19 01:14:08 -05:00

225 lines
6.8 KiB
C++

#include "nvprefs_common.h"
#include "nvprefs_interface.h"
#include "driver_settings.h"
#include "undo_data.h"
#include "undo_file.h"
namespace {
const auto sunshine_program_data_folder = "Sunshine";
const auto nvprefs_undo_file_name = "nvprefs_undo.json";
} // namespace
namespace nvprefs {
struct nvprefs_interface::impl {
bool loaded = false;
driver_settings_t driver_settings;
std::filesystem::path undo_folder_path;
std::filesystem::path undo_file_path;
std::optional<undo_data_t> undo_data;
std::optional<undo_file_t> undo_file;
};
nvprefs_interface::nvprefs_interface():
pimpl(new impl()) {
}
nvprefs_interface::~nvprefs_interface() {
if (owning_undo_file() && load()) {
restore_global_profile();
}
unload();
}
bool
nvprefs_interface::load() {
if (!pimpl->loaded) {
// Check %ProgramData% variable, need it for storing undo file
wchar_t program_data_env[MAX_PATH];
auto get_env_result = GetEnvironmentVariableW(L"ProgramData", program_data_env, MAX_PATH);
if (get_env_result == 0 || get_env_result >= MAX_PATH || !std::filesystem::is_directory(program_data_env)) {
error_message("Missing or malformed %ProgramData% environment variable");
return false;
}
// Prepare undo file path variables
pimpl->undo_folder_path = std::filesystem::path(program_data_env) / sunshine_program_data_folder;
pimpl->undo_file_path = pimpl->undo_folder_path / nvprefs_undo_file_name;
// Dynamically load nvapi library and load driver settings
pimpl->loaded = pimpl->driver_settings.init();
}
return pimpl->loaded;
}
void
nvprefs_interface::unload() {
if (pimpl->loaded) {
// Unload dynamically loaded nvapi library
pimpl->driver_settings.destroy();
pimpl->loaded = false;
}
}
bool
nvprefs_interface::restore_from_and_delete_undo_file_if_exists() {
if (!pimpl->loaded) return false;
// Check for undo file from previous improper termination
bool access_denied = false;
if (auto undo_file = undo_file_t::open_existing_file(pimpl->undo_file_path, access_denied)) {
// Try to restore from the undo file
info_message("Opened undo file from previous improper termination");
if (auto undo_data = undo_file->read_undo_data()) {
if (pimpl->driver_settings.restore_global_profile_to_undo(*undo_data) && pimpl->driver_settings.save_settings()) {
info_message("Restored global profile settings from undo file - deleting the file");
}
else {
error_message("Failed to restore global profile settings from undo file, deleting the file anyway");
}
}
else {
error_message("Coulnd't read undo file, deleting the file anyway");
}
if (!undo_file->delete_file()) {
error_message("Couldn't delete undo file");
return false;
}
}
else if (access_denied) {
error_message("Couldn't open undo file from previous improper termination, or confirm that there's no such file");
return false;
}
return true;
}
bool
nvprefs_interface::modify_application_profile() {
if (!pimpl->loaded) return false;
// Modify and save sunshine.exe application profile settings, if needed
bool modified = false;
if (!pimpl->driver_settings.check_and_modify_application_profile(modified)) {
error_message("Failed to modify application profile settings");
return false;
}
else if (modified) {
if (pimpl->driver_settings.save_settings()) {
info_message("Modified application profile settings");
}
else {
error_message("Couldn't save application profile settings");
return false;
}
}
else {
info_message("No need to modify application profile settings");
}
return true;
}
bool
nvprefs_interface::modify_global_profile() {
if (!pimpl->loaded) return false;
// Modify but not save global profile settings, if needed
std::optional<undo_data_t> undo_data;
if (!pimpl->driver_settings.check_and_modify_global_profile(undo_data)) {
error_message("Couldn't modify global profile settings");
return false;
}
else if (!undo_data) {
info_message("No need to modify global profile settings");
return true;
}
auto make_undo_and_commit = [&]() -> bool {
// Create and lock undo file if it hasn't been done yet
if (!pimpl->undo_file) {
// Prepare Sunshine folder in ProgramData if it doesn't exist
if (!CreateDirectoryW(pimpl->undo_folder_path.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
error_message("Couldn't create undo folder");
return false;
}
// Create undo file to handle improper termination of nvprefs.exe
pimpl->undo_file = undo_file_t::create_new_file(pimpl->undo_file_path);
if (!pimpl->undo_file) {
error_message("Couldn't create undo file");
return false;
}
}
assert(undo_data);
if (pimpl->undo_data) {
// Merge undo data if settings has been modified externally since our last modification
pimpl->undo_data->merge(*undo_data);
}
else {
pimpl->undo_data = undo_data;
}
// Write undo data to undo file
if (!pimpl->undo_file->write_undo_data(*pimpl->undo_data)) {
error_message("Couldn't write to undo file - deleting the file");
if (!pimpl->undo_file->delete_file()) {
error_message("Couldn't delete undo file");
}
return false;
}
// Save global profile settings
if (!pimpl->driver_settings.save_settings()) {
error_message("Couldn't save global profile settings");
return false;
}
return true;
};
if (!make_undo_and_commit()) {
// Revert settings modifications
pimpl->driver_settings.load_settings();
return false;
}
return true;
}
bool
nvprefs_interface::owning_undo_file() {
return pimpl->undo_file.has_value();
}
bool
nvprefs_interface::restore_global_profile() {
if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) return false;
// Restore global profile settings with undo data
if (pimpl->driver_settings.restore_global_profile_to_undo(*pimpl->undo_data) &&
pimpl->driver_settings.save_settings()) {
// Global profile settings sucessfully restored, can delete undo file
if (!pimpl->undo_file->delete_file()) {
error_message("Couldn't delete undo file");
return false;
}
pimpl->undo_data = std::nullopt;
pimpl->undo_file = std::nullopt;
}
else {
error_message("Couldn't restore global profile settings");
return false;
}
return true;
}
} // namespace nvprefs