mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-03-10 07:14:23 +00:00
Modify and restore NVIDIA control panel settings
Namely: - Power profile for sunshine.exe - Global OpenGL/Vulkan DXGI swapchain
This commit is contained in:
parent
6992d424a8
commit
f245f777f7
@ -134,10 +134,12 @@ pkg_check_modules(CURL REQUIRED libcurl)
|
||||
|
||||
if(WIN32)
|
||||
set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103
|
||||
# 1.82.0+ is required for boost::json::value::set_at_pointer() support
|
||||
find_package(Boost 1.82.0 COMPONENTS locale log filesystem program_options json REQUIRED)
|
||||
else()
|
||||
find_package(Boost COMPONENTS locale log filesystem program_options REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Boost COMPONENTS locale log filesystem program_options REQUIRED)
|
||||
|
||||
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wall -Wno-sign-compare)
|
||||
|
||||
# enable system tray, we will disable this later if we cannot find the required package config on linux
|
||||
@ -155,6 +157,12 @@ if(WIN32)
|
||||
add_compile_definitions(SUNSHINE_PLATFORM="windows")
|
||||
add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now
|
||||
|
||||
include_directories(SYSTEM third-party/nvapi-opensource)
|
||||
file(GLOB NVPREFS_FILES CONFIGURE_DEPENDS
|
||||
"third-party/nvapi-opensource/*.h"
|
||||
"src/platform/windows/nvprefs/*.cpp"
|
||||
"src/platform/windows/nvprefs/*.h")
|
||||
|
||||
include_directories(SYSTEM third-party/ViGEmClient/include)
|
||||
|
||||
if(NOT DEFINED SUNSHINE_ICON_PATH)
|
||||
@ -177,7 +185,8 @@ if(WIN32)
|
||||
third-party/ViGEmClient/include/ViGEm/Client.h
|
||||
third-party/ViGEmClient/include/ViGEm/Common.h
|
||||
third-party/ViGEmClient/include/ViGEm/Util.h
|
||||
third-party/ViGEmClient/include/ViGEm/km/BusShared.h)
|
||||
third-party/ViGEmClient/include/ViGEm/km/BusShared.h
|
||||
${NVPREFS_FILES})
|
||||
|
||||
set(OPENSSL_LIBRARIES
|
||||
libssl.a
|
||||
@ -762,6 +771,7 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"'
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"'
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\sunshine.exe\\\" --restore-nvprefs-undo'
|
||||
MessageBox MB_YESNO|MB_ICONQUESTION \
|
||||
'Do you want to remove ViGEmBus)?' \
|
||||
/SD IDNO IDNO NoVigem
|
||||
|
49
src/main.cpp
49
src/main.cpp
@ -46,6 +46,11 @@ safe::mail_t mail::man;
|
||||
using namespace std::literals;
|
||||
namespace bl = boost::log;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Define global singleton used for NVIDIA control panel modifications
|
||||
nvprefs::nvprefs_interface nvprefs_instance;
|
||||
#endif
|
||||
|
||||
thread_pool_util::ThreadPool task_pool;
|
||||
bl::sources::severity_logger<int> verbose(0); // Dominating output
|
||||
bl::sources::severity_logger<int> debug(1); // Follow what is happening
|
||||
@ -112,6 +117,22 @@ namespace version {
|
||||
}
|
||||
} // namespace version
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace restore_nvprefs_undo {
|
||||
int
|
||||
entry(const char *name, int argc, char *argv[]) {
|
||||
// Restore global NVIDIA control panel settings to the undo file
|
||||
// left by improper termination of sunshine.exe, if it exists.
|
||||
// This entry point is typically called by the uninstaller.
|
||||
if (nvprefs_instance.load()) {
|
||||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace restore_nvprefs_undo
|
||||
#endif
|
||||
|
||||
namespace lifetime {
|
||||
static char **argv;
|
||||
static std::atomic_int desired_exit_code;
|
||||
@ -413,7 +434,10 @@ namespace gen_creds {
|
||||
std::map<std::string_view, std::function<int(const char *name, int argc, char **argv)>> cmd_to_func {
|
||||
{ "creds"sv, gen_creds::entry },
|
||||
{ "help"sv, help::entry },
|
||||
{ "version"sv, version::entry }
|
||||
{ "version"sv, version::entry },
|
||||
#ifdef _WIN32
|
||||
{ "restore-nvprefs-undo"sv, restore_nvprefs_undo::entry },
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -568,6 +592,21 @@ main(int argc, char *argv[]) {
|
||||
|
||||
return fn->second(argv[0], config::sunshine.cmd.argc, config::sunshine.cmd.argv);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// Modify relevant NVIDIA control panel settings if the system has corresponding gpu
|
||||
if (nvprefs_instance.load()) {
|
||||
// Restore global settings to the undo file left by improper termination of sunshine.exe
|
||||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
|
||||
// Modify application settings for sunshine.exe
|
||||
nvprefs_instance.modify_application_profile();
|
||||
// Modify global settings, undo file is produced in the process to restore after improper termination
|
||||
nvprefs_instance.modify_global_profile();
|
||||
// Unload dynamic library to survive driver reinstallation
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_LOG(info) << PROJECT_NAME << " version: " << PROJECT_VER << std::endl;
|
||||
task_pool.start(1);
|
||||
|
||||
@ -675,6 +714,14 @@ main(int argc, char *argv[]) {
|
||||
system_tray::end_tray();
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
// Restore global NVIDIA control panel settings
|
||||
if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) {
|
||||
nvprefs_instance.restore_global_profile();
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
#endif
|
||||
|
||||
return lifetime::desired_exit_code;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,12 @@
|
||||
#include "thread_pool.h"
|
||||
#include "thread_safe.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// Declare global singleton used for NVIDIA control panel modifications
|
||||
#include "platform/windows/nvprefs/nvprefs_interface.h"
|
||||
extern nvprefs::nvprefs_interface nvprefs_instance;
|
||||
#endif
|
||||
|
||||
extern thread_pool_util::ThreadPool task_pool;
|
||||
extern bool display_cursor;
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include "src/utility.h"
|
||||
#include <iterator>
|
||||
|
||||
#include "nvprefs/nvprefs_interface.h"
|
||||
|
||||
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
||||
#ifndef UDP_SEND_MSG_SIZE
|
||||
#define UDP_SEND_MSG_SIZE 2
|
||||
@ -740,6 +742,16 @@ namespace platf {
|
||||
// Promote ourselves to high priority class
|
||||
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
||||
|
||||
// Modify NVIDIA control panel settings again, in case they have been changed externally since sunshine launch
|
||||
if (nvprefs_instance.load()) {
|
||||
if (!nvprefs_instance.owning_undo_file()) {
|
||||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
|
||||
}
|
||||
nvprefs_instance.modify_application_profile();
|
||||
nvprefs_instance.modify_global_profile();
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
|
||||
// Enable low latency mode on all connected WLAN NICs if wlanapi.dll is available
|
||||
if (fn_WlanOpenHandle) {
|
||||
DWORD negotiated_version;
|
||||
|
289
src/platform/windows/nvprefs/driver_settings.cpp
Normal file
289
src/platform/windows/nvprefs/driver_settings.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "driver_settings.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const auto sunshine_application_profile_name = L"SunshineStream";
|
||||
const auto sunshine_application_path = L"sunshine.exe";
|
||||
|
||||
void
|
||||
nvapi_error_message(NvAPI_Status status) {
|
||||
NvAPI_ShortString message = {};
|
||||
NvAPI_GetErrorMessage(status, message);
|
||||
nvprefs::error_message(std::string("NvAPI error: ") + message);
|
||||
}
|
||||
|
||||
void
|
||||
fill_nvapi_string(NvAPI_UnicodeString &dest, const wchar_t *src) {
|
||||
static_assert(sizeof(NvU16) == sizeof(wchar_t));
|
||||
memcpy_s(dest, NVAPI_UNICODE_STRING_MAX * sizeof(NvU16), src, (wcslen(src) + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
driver_settings_t::~driver_settings_t() {
|
||||
if (session_handle) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::init() {
|
||||
if (session_handle) return true;
|
||||
|
||||
NvAPI_Status status;
|
||||
|
||||
status = NvAPI_Initialize();
|
||||
if (status != NVAPI_OK) {
|
||||
info_message("NvAPI_Initialize() failed, ignore if you don't have NVIDIA video card");
|
||||
return false;
|
||||
}
|
||||
|
||||
status = NvAPI_DRS_CreateSession(&session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_CreateSession() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return load_settings();
|
||||
}
|
||||
|
||||
void
|
||||
driver_settings_t::destroy() {
|
||||
if (session_handle) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
session_handle = 0;
|
||||
}
|
||||
NvAPI_Unload();
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::load_settings() {
|
||||
if (!session_handle) return false;
|
||||
|
||||
NvAPI_Status status = NvAPI_DRS_LoadSettings(session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_LoadSettings() failed");
|
||||
destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::save_settings() {
|
||||
if (!session_handle) return false;
|
||||
|
||||
NvAPI_Status status = NvAPI_DRS_SaveSettings(session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SaveSettings() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::restore_global_profile_to_undo(const undo_data_t &undo_data) {
|
||||
if (!session_handle) return false;
|
||||
|
||||
auto [opengl_swapchain_saved, opengl_swapchain_our_value, opengl_swapchain_undo_value] = undo_data.get_opengl_swapchain();
|
||||
|
||||
if (opengl_swapchain_saved) {
|
||||
NvAPI_Status status;
|
||||
|
||||
NvDRSProfileHandle profile_handle = 0;
|
||||
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetBaseProfile() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
NVDRS_SETTING setting = {};
|
||||
setting.version = NVDRS_SETTING_VER;
|
||||
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID, &setting);
|
||||
|
||||
if (status == NVAPI_OK && setting.settingLocation == NVDRS_CURRENT_PROFILE_LOCATION && setting.u32CurrentValue == opengl_swapchain_our_value) {
|
||||
if (opengl_swapchain_undo_value) {
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
setting.settingId = OGL_CPL_PREFER_DXPRESENT_ID;
|
||||
setting.settingType = NVDRS_DWORD_TYPE;
|
||||
setting.settingLocation = NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
setting.u32CurrentValue = *opengl_swapchain_undo_value;
|
||||
|
||||
status = NvAPI_DRS_SetSetting(session_handle, profile_handle, &setting);
|
||||
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = NvAPI_DRS_DeleteProfileSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID);
|
||||
|
||||
if (status != NVAPI_OK && status != NVAPI_SETTING_NOT_FOUND) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_DeleteProfileSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
info_message("Restored OGL_CPL_PREFER_DXPRESENT for base profile");
|
||||
}
|
||||
else if (status == NVAPI_OK || status == NVAPI_SETTING_NOT_FOUND) {
|
||||
info_message("OGL_CPL_PREFER_DXPRESENT has been changed from our value in base profile, not restoring");
|
||||
}
|
||||
else {
|
||||
error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::check_and_modify_global_profile(std::optional<undo_data_t> &undo_data) {
|
||||
if (!session_handle) return false;
|
||||
|
||||
undo_data.reset();
|
||||
NvAPI_Status status;
|
||||
|
||||
NvDRSProfileHandle profile_handle = 0;
|
||||
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetBaseProfile() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
NVDRS_SETTING setting = {};
|
||||
setting.version = NVDRS_SETTING_VER;
|
||||
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID, &setting);
|
||||
|
||||
// Remember current OpenGL/Vulkan DXGI swapchain setting and change it if needed
|
||||
if (status == NVAPI_SETTING_NOT_FOUND || (status == NVAPI_OK && setting.u32CurrentValue != OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED)) {
|
||||
undo_data = undo_data_t();
|
||||
if (status == NVAPI_OK) {
|
||||
undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, setting.u32CurrentValue);
|
||||
}
|
||||
else {
|
||||
undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, std::nullopt);
|
||||
}
|
||||
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
setting.settingId = OGL_CPL_PREFER_DXPRESENT_ID;
|
||||
setting.settingType = NVDRS_DWORD_TYPE;
|
||||
setting.settingLocation = NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
setting.u32CurrentValue = OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED;
|
||||
|
||||
status = NvAPI_DRS_SetSetting(session_handle, profile_handle, &setting);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
info_message("Changed OGL_CPL_PREFER_DXPRESENT to OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED for base profile");
|
||||
}
|
||||
else if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::check_and_modify_application_profile(bool &modified) {
|
||||
if (!session_handle) return false;
|
||||
|
||||
modified = false;
|
||||
NvAPI_Status status;
|
||||
|
||||
NvAPI_UnicodeString profile_name = {};
|
||||
fill_nvapi_string(profile_name, sunshine_application_profile_name);
|
||||
|
||||
NvDRSProfileHandle profile_handle = 0;
|
||||
status = NvAPI_DRS_FindProfileByName(session_handle, profile_name, &profile_handle);
|
||||
|
||||
if (status != NVAPI_OK) {
|
||||
// Create application profile if missing
|
||||
NVDRS_PROFILE profile = {};
|
||||
profile.version = NVDRS_PROFILE_VER1;
|
||||
fill_nvapi_string(profile.profileName, sunshine_application_profile_name);
|
||||
status = NvAPI_DRS_CreateProfile(session_handle, &profile, &profile_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_CreateProfile() failed");
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
NvAPI_UnicodeString sunshine_path = {};
|
||||
fill_nvapi_string(sunshine_path, sunshine_application_path);
|
||||
|
||||
NVDRS_APPLICATION application = {};
|
||||
application.version = NVDRS_APPLICATION_VER_V1;
|
||||
status = NvAPI_DRS_GetApplicationInfo(session_handle, profile_handle, sunshine_path, &application);
|
||||
|
||||
if (status != NVAPI_OK) {
|
||||
// Add application to application profile if missing
|
||||
application.version = NVDRS_APPLICATION_VER_V1;
|
||||
application.isPredefined = 0;
|
||||
fill_nvapi_string(application.appName, sunshine_application_path);
|
||||
fill_nvapi_string(application.userFriendlyName, sunshine_application_path);
|
||||
fill_nvapi_string(application.launcher, L"");
|
||||
|
||||
status = NvAPI_DRS_CreateApplication(session_handle, profile_handle, &application);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_CreateApplication() failed");
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
NVDRS_SETTING setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, PREFERRED_PSTATE_ID, &setting);
|
||||
|
||||
if (status != NVAPI_OK ||
|
||||
setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION ||
|
||||
setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) {
|
||||
// Set power setting if needed
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
setting.settingId = PREFERRED_PSTATE_ID;
|
||||
setting.settingType = NVDRS_DWORD_TYPE;
|
||||
setting.settingLocation = NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
setting.u32CurrentValue = PREFERRED_PSTATE_PREFER_MAX;
|
||||
|
||||
status = NvAPI_DRS_SetSetting(session_handle, profile_handle, &setting);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SetSetting() PREFERRED_PSTATE failed");
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
|
||||
info_message(std::wstring(L"Changed PREFERRED_PSTATE to PREFERRED_PSTATE_PREFER_MAX for ") + sunshine_application_path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
36
src/platform/windows/nvprefs/driver_settings.h
Normal file
36
src/platform/windows/nvprefs/driver_settings.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "undo_data.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class driver_settings_t {
|
||||
public:
|
||||
~driver_settings_t();
|
||||
|
||||
bool
|
||||
init();
|
||||
|
||||
void
|
||||
destroy();
|
||||
|
||||
bool
|
||||
load_settings();
|
||||
|
||||
bool
|
||||
save_settings();
|
||||
|
||||
bool
|
||||
restore_global_profile_to_undo(const undo_data_t &undo_data);
|
||||
|
||||
bool
|
||||
check_and_modify_global_profile(std::optional<undo_data_t> &undo_data);
|
||||
|
||||
bool
|
||||
check_and_modify_application_profile(bool &modified);
|
||||
|
||||
private:
|
||||
NvDRSSessionHandle session_handle = 0;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
128
src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp
Normal file
128
src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include <nvapi_interface.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<const char *, void *> interfaces;
|
||||
HMODULE dll = NULL;
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
NvAPI_Status
|
||||
call_interface(const char *name, Args... args) {
|
||||
auto func = (Func *) interfaces[name];
|
||||
|
||||
if (!func) {
|
||||
return interfaces.empty() ? NVAPI_API_NOT_INITIALIZED : NVAPI_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return func(args...);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#undef NVAPI_INTERFACE
|
||||
#define NVAPI_INTERFACE NvAPI_Status __cdecl
|
||||
|
||||
extern void *
|
||||
nvapi_QueryInterface(NvU32 id);
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_Initialize() {
|
||||
if (dll) return NVAPI_OK;
|
||||
|
||||
#ifdef _WIN64
|
||||
auto dll_name = "nvapi64.dll";
|
||||
#else
|
||||
auto dll_name = "nvapi.dll";
|
||||
#endif
|
||||
|
||||
if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
|
||||
if (auto query_interface = (decltype(nvapi_QueryInterface) *) GetProcAddress(dll, "nvapi_QueryInterface")) {
|
||||
for (const auto &item : nvapi_interface_table) {
|
||||
interfaces[item.func] = query_interface(item.id);
|
||||
}
|
||||
return NVAPI_OK;
|
||||
}
|
||||
}
|
||||
|
||||
NvAPI_Unload();
|
||||
return NVAPI_LIBRARY_NOT_FOUND;
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_Unload() {
|
||||
if (dll) {
|
||||
interfaces.clear();
|
||||
FreeLibrary(dll);
|
||||
dll = NULL;
|
||||
}
|
||||
return NVAPI_OK;
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_GetErrorMessage(NvAPI_Status nr, NvAPI_ShortString szDesc) {
|
||||
return call_interface<decltype(NvAPI_GetErrorMessage)>("NvAPI_GetErrorMessage", nr, szDesc);
|
||||
}
|
||||
|
||||
// This is only a subset of NvAPI_DRS_* functions, more can be added if needed
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateSession(NvDRSSessionHandle *phSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateSession)>("NvAPI_DRS_CreateSession", phSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_DestroySession(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_DestroySession)>("NvAPI_DRS_DestroySession", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_LoadSettings(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_LoadSettings)>("NvAPI_DRS_LoadSettings", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_SaveSettings(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_SaveSettings)>("NvAPI_DRS_SaveSettings", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateProfile(NvDRSSessionHandle hSession, NVDRS_PROFILE *pProfileInfo, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateProfile)>("NvAPI_DRS_CreateProfile", hSession, pProfileInfo, phProfile);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_FindProfileByName(NvDRSSessionHandle hSession, NvAPI_UnicodeString profileName, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_FindProfileByName)>("NvAPI_DRS_FindProfileByName", hSession, profileName, phProfile);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateApplication(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_APPLICATION *pApplication) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateApplication)>("NvAPI_DRS_CreateApplication", hSession, hProfile, pApplication);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetApplicationInfo(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvAPI_UnicodeString appName, NVDRS_APPLICATION *pApplication) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetApplicationInfo)>("NvAPI_DRS_GetApplicationInfo", hSession, hProfile, appName, pApplication);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_SetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_SETTING *pSetting) {
|
||||
return call_interface<decltype(NvAPI_DRS_SetSetting)>("NvAPI_DRS_SetSetting", hSession, hProfile, pSetting);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId, NVDRS_SETTING *pSetting) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetSetting)>("NvAPI_DRS_GetSetting", hSession, hProfile, settingId, pSetting);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_DeleteProfileSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId) {
|
||||
return call_interface<decltype(NvAPI_DRS_DeleteProfileSetting)>("NvAPI_DRS_DeleteProfileSetting", hSession, hProfile, settingId);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetBaseProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetBaseProfile)>("NvAPI_DRS_GetBaseProfile", hSession, phProfile);
|
||||
}
|
25
src/platform/windows/nvprefs/nvprefs_common.cpp
Normal file
25
src/platform/windows/nvprefs/nvprefs_common.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
void
|
||||
info_message(const std::wstring &message) {
|
||||
BOOST_LOG(info) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
info_message(const std::string &message) {
|
||||
BOOST_LOG(info) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
error_message(const std::wstring &message) {
|
||||
BOOST_LOG(error) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
error_message(const std::string &message) {
|
||||
BOOST_LOG(error) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
70
src/platform/windows/nvprefs/nvprefs_common.h
Normal file
70
src/platform/windows/nvprefs/nvprefs_common.h
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
// sunshine utility header for generic smart pointers
|
||||
#include "src/utility.h"
|
||||
|
||||
// sunshine boost::log severity levels
|
||||
#include "src/main.h"
|
||||
|
||||
// standard library headers
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
// winapi headers
|
||||
// disable clang-format header reordering
|
||||
// clang-format off
|
||||
#include <windows.h>
|
||||
#include <aclapi.h>
|
||||
// clang-format on
|
||||
|
||||
// nvapi headers
|
||||
// disable clang-format header reordering
|
||||
// clang-format off
|
||||
#include <nvapi.h>
|
||||
#include <NvApiDriverSettings.h>
|
||||
// clang-format on
|
||||
|
||||
// boost headers
|
||||
#include <boost/json.hpp>
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
struct safe_handle: public util::safe_ptr_v2<void, BOOL, CloseHandle> {
|
||||
using util::safe_ptr_v2<void, BOOL, CloseHandle>::safe_ptr_v2;
|
||||
explicit operator bool() const {
|
||||
auto handle = get();
|
||||
return handle != NULL && handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
};
|
||||
|
||||
struct safe_hlocal_deleter {
|
||||
void
|
||||
operator()(void *p) {
|
||||
LocalFree(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using safe_hlocal = util::uniq_ptr<std::remove_pointer_t<T>, safe_hlocal_deleter>;
|
||||
|
||||
using safe_sid = util::safe_ptr_v2<void, PVOID, FreeSid>;
|
||||
|
||||
void
|
||||
info_message(const std::wstring &message);
|
||||
|
||||
void
|
||||
info_message(const std::string &message);
|
||||
|
||||
void
|
||||
error_message(const std::wstring &message);
|
||||
|
||||
void
|
||||
error_message(const std::string &message);
|
||||
|
||||
} // namespace nvprefs
|
225
src/platform/windows/nvprefs/nvprefs_interface.cpp
Normal file
225
src/platform/windows/nvprefs/nvprefs_interface.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
#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
|
36
src/platform/windows/nvprefs/nvprefs_interface.h
Normal file
36
src/platform/windows/nvprefs/nvprefs_interface.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class nvprefs_interface {
|
||||
public:
|
||||
nvprefs_interface();
|
||||
~nvprefs_interface();
|
||||
|
||||
bool
|
||||
load();
|
||||
|
||||
void
|
||||
unload();
|
||||
|
||||
bool
|
||||
restore_from_and_delete_undo_file_if_exists();
|
||||
|
||||
bool
|
||||
modify_application_profile();
|
||||
|
||||
bool
|
||||
modify_global_profile();
|
||||
|
||||
bool
|
||||
owning_undo_file();
|
||||
|
||||
bool
|
||||
restore_global_profile();
|
||||
|
||||
private:
|
||||
struct impl;
|
||||
std::unique_ptr<impl> pimpl;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
71
src/platform/windows/nvprefs/undo_data.cpp
Normal file
71
src/platform/windows/nvprefs/undo_data.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "undo_data.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const auto opengl_swapchain_our_value_key = "/opengl_swapchain/our_value";
|
||||
const auto opengl_swapchain_undo_value_key = "/opengl_swapchain/undo_value";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
void
|
||||
undo_data_t::set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value) {
|
||||
data.set_at_pointer(opengl_swapchain_our_value_key, our_value);
|
||||
if (undo_value) {
|
||||
data.set_at_pointer(opengl_swapchain_undo_value_key, *undo_value);
|
||||
}
|
||||
else {
|
||||
data.set_at_pointer(opengl_swapchain_undo_value_key, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<bool, uint32_t, std::optional<uint32_t>>
|
||||
undo_data_t::get_opengl_swapchain() const {
|
||||
auto get_value = [this](const auto &key) -> std::tuple<bool, std::optional<uint32_t>> {
|
||||
try {
|
||||
auto value = data.at_pointer(key);
|
||||
if (value.is_null()) {
|
||||
return { true, std::nullopt };
|
||||
}
|
||||
else if (value.is_number()) {
|
||||
return { true, value.template to_number<uint32_t>() };
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
error_message(std::string("Couldn't find ") + key + " element");
|
||||
return { false, std::nullopt };
|
||||
};
|
||||
|
||||
auto [our_value_present, our_value] = get_value(opengl_swapchain_our_value_key);
|
||||
auto [undo_value_present, undo_value] = get_value(opengl_swapchain_undo_value_key);
|
||||
|
||||
if (!our_value_present || !undo_value_present || !our_value) {
|
||||
return { false, 0, std::nullopt };
|
||||
}
|
||||
|
||||
return { true, *our_value, undo_value };
|
||||
}
|
||||
|
||||
std::string
|
||||
undo_data_t::write() const {
|
||||
return boost::json::serialize(data);
|
||||
}
|
||||
|
||||
void
|
||||
undo_data_t::read(const std::vector<char> &buffer) {
|
||||
data = boost::json::parse(std::string_view(buffer.data(), buffer.size()));
|
||||
}
|
||||
|
||||
void
|
||||
undo_data_t::merge(const undo_data_t &newer_data) {
|
||||
auto [opengl_swapchain_saved, opengl_swapchain_our_value, opengl_swapchain_undo_value] = newer_data.get_opengl_swapchain();
|
||||
if (opengl_swapchain_saved) {
|
||||
set_opengl_swapchain(opengl_swapchain_our_value, opengl_swapchain_undo_value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
32
src/platform/windows/nvprefs/undo_data.h
Normal file
32
src/platform/windows/nvprefs/undo_data.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class undo_data_t {
|
||||
public:
|
||||
void
|
||||
set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value);
|
||||
|
||||
std::tuple<bool, uint32_t, std::optional<uint32_t>>
|
||||
get_opengl_swapchain() const;
|
||||
|
||||
void
|
||||
write(std::ostream &stream) const;
|
||||
|
||||
std::string
|
||||
write() const;
|
||||
|
||||
void
|
||||
read(std::istream &stream);
|
||||
|
||||
void
|
||||
read(const std::vector<char> &buffer);
|
||||
|
||||
void
|
||||
merge(const undo_data_t &newer_data);
|
||||
|
||||
private:
|
||||
boost::json::value data;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
154
src/platform/windows/nvprefs/undo_file.cpp
Normal file
154
src/platform/windows/nvprefs/undo_file.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "undo_file.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace nvprefs;
|
||||
|
||||
DWORD
|
||||
relax_permissions(HANDLE file_handle) {
|
||||
PACL old_dacl = nullptr;
|
||||
|
||||
safe_hlocal<PSECURITY_DESCRIPTOR> sd;
|
||||
DWORD status = GetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &old_dacl, nullptr, &sd);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
|
||||
safe_sid users_sid;
|
||||
SID_IDENTIFIER_AUTHORITY nt_authorithy = SECURITY_NT_AUTHORITY;
|
||||
if (!AllocateAndInitializeSid(&nt_authorithy, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &users_sid)) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
EXPLICIT_ACCESS ea = {};
|
||||
ea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE | DELETE;
|
||||
ea.grfAccessMode = GRANT_ACCESS;
|
||||
ea.grfInheritance = NO_INHERITANCE;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.ptstrName = (LPTSTR) users_sid.get();
|
||||
|
||||
safe_hlocal<PACL> new_dacl;
|
||||
status = SetEntriesInAcl(1, &ea, old_dacl, &new_dacl);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
|
||||
status = SetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, new_dacl.get(), nullptr);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
std::optional<undo_file_t>
|
||||
undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) {
|
||||
undo_file_t file;
|
||||
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_READ | DELETE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
if (file.file_handle) {
|
||||
access_denied = false;
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
auto last_error = GetLastError();
|
||||
access_denied = (last_error != ERROR_FILE_NOT_FOUND && last_error != ERROR_PATH_NOT_FOUND);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<undo_file_t>
|
||||
undo_file_t::create_new_file(std::filesystem::path file_path) {
|
||||
undo_file_t file;
|
||||
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_WRITE | STANDARD_RIGHTS_ALL, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
|
||||
if (file.file_handle) {
|
||||
// give GENERIC_READ, GENERIC_WRITE and DELETE permissions to Users group
|
||||
if (relax_permissions(file.file_handle.get()) != 0) {
|
||||
error_message("Failed to relax permissions on undo file");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
undo_file_t::delete_file() {
|
||||
if (!file_handle) return false;
|
||||
|
||||
FILE_DISPOSITION_INFO delete_file_info = { TRUE };
|
||||
if (SetFileInformationByHandle(file_handle.get(), FileDispositionInfo, &delete_file_info, sizeof(delete_file_info))) {
|
||||
file_handle.reset();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
undo_file_t::write_undo_data(const undo_data_t &undo_data) {
|
||||
if (!file_handle) return false;
|
||||
|
||||
std::string buffer;
|
||||
try {
|
||||
buffer = undo_data.write();
|
||||
}
|
||||
catch (...) {
|
||||
error_message("Couldn't serialize undo data");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetFilePointerEx(file_handle.get(), {}, nullptr, FILE_BEGIN) || !SetEndOfFile(file_handle.get())) {
|
||||
error_message("Couldn't clear undo file");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD bytes_written = 0;
|
||||
if (!WriteFile(file_handle.get(), buffer.data(), buffer.size(), &bytes_written, nullptr) || bytes_written != buffer.size()) {
|
||||
error_message("Couldn't write undo file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FlushFileBuffers(file_handle.get())) {
|
||||
error_message("Failed to flush undo file");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<undo_data_t>
|
||||
undo_file_t::read_undo_data() {
|
||||
if (!file_handle) return std::nullopt;
|
||||
|
||||
LARGE_INTEGER file_size;
|
||||
if (!GetFileSizeEx(file_handle.get(), &file_size)) {
|
||||
error_message("Couldn't get undo file size");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if ((size_t) file_size.QuadPart > 1024) {
|
||||
error_message("Undo file size is unexpectedly large, aborting");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<char> buffer(file_size.QuadPart);
|
||||
DWORD bytes_read = 0;
|
||||
if (!ReadFile(file_handle.get(), buffer.data(), buffer.size(), &bytes_read, nullptr) || bytes_read != buffer.size()) {
|
||||
error_message("Couldn't read undo file");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
undo_data_t undo_data;
|
||||
try {
|
||||
undo_data.read(buffer);
|
||||
}
|
||||
catch (...) {
|
||||
error_message("Couldn't parse undo file");
|
||||
return std::nullopt;
|
||||
}
|
||||
return undo_data;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
29
src/platform/windows/nvprefs/undo_file.h
Normal file
29
src/platform/windows/nvprefs/undo_file.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "undo_data.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class undo_file_t {
|
||||
public:
|
||||
static std::optional<undo_file_t>
|
||||
open_existing_file(std::filesystem::path file_path, bool &access_denied);
|
||||
|
||||
static std::optional<undo_file_t>
|
||||
create_new_file(std::filesystem::path file_path);
|
||||
|
||||
bool
|
||||
delete_file();
|
||||
|
||||
bool
|
||||
write_undo_data(const undo_data_t &undo_data);
|
||||
|
||||
std::optional<undo_data_t>
|
||||
read_undo_data();
|
||||
|
||||
private:
|
||||
undo_file_t() = default;
|
||||
safe_handle file_handle;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
Loading…
x
Reference in New Issue
Block a user