server/tools/display_helper_paths.h
Rama main · réplica de lectura del repositorio original.
#pragma once
/**
* @file tools/display_helper_paths.h
* @brief Shared path/singleton helpers for the display helper engines.
*
* Both the legacy engine and the v2 engine must agree on snapshot file locations,
* the search order for state files, and the single-instance mutex so that either
* engine can pick up the other's persisted state (drop-in engine switching).
* Bodies are ported verbatim from tools/display_settings_helper.cpp.
*/
#ifdef _WIN32
#include <algorithm>
#include <cwchar>
#include <filesystem>
#include <string>
#include <vector>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
#include "src/platform/windows/ipc/misc_utils.h"
namespace display_helper_paths {
inline HANDLE make_named_mutex(const wchar_t *name) {
SECURITY_ATTRIBUTES sa {};
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
return CreateMutexW(&sa, FALSE, name);
}
inline bool ensure_single_instance(HANDLE &out_handle) {
out_handle = make_named_mutex(L"Global\\SunshineDisplayHelper");
if (!out_handle && GetLastError() == ERROR_ACCESS_DENIED) {
out_handle = make_named_mutex(L"Local\\SunshineDisplayHelper");
}
if (!out_handle) {
return true; // continue; best-effort singleton failed
}
if (GetLastError() == ERROR_ALREADY_EXISTS) {
return false; // another instance running
}
return true;
}
inline void hide_console_window() {
if (HWND console = GetConsoleWindow()) {
ShowWindow(console, SW_HIDE);
}
}
inline std::filesystem::path compute_log_dir() {
// Try roaming AppData first
std::wstring appdataW;
appdataW.resize(MAX_PATH);
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, appdataW.data()))) {
appdataW.resize(wcslen(appdataW.c_str()));
auto path = std::filesystem::path(appdataW) / L"Sunshine";
std::error_code ec;
std::filesystem::create_directories(path, ec);
return path;
}
// Next, %APPDATA%
std::wstring envAppData;
DWORD needed = GetEnvironmentVariableW(L"APPDATA", nullptr, 0);
if (needed > 0) {
envAppData.resize(needed);
DWORD written = GetEnvironmentVariableW(L"APPDATA", envAppData.data(), needed);
if (written > 0) {
envAppData.resize(written);
auto path = std::filesystem::path(envAppData) / L"Sunshine";
std::error_code ec;
std::filesystem::create_directories(path, ec);
return path;
}
}
// Fallback: temp directory or current dir
std::wstring tempW;
tempW.resize(MAX_PATH);
DWORD tlen = GetTempPathW(MAX_PATH, tempW.data());
if (tlen > 0 && tlen < MAX_PATH) {
tempW.resize(tlen);
auto path = std::filesystem::path(tempW) / L"Sunshine";
std::error_code ec;
std::filesystem::create_directories(path, ec);
return path;
}
auto path = std::filesystem::path(L".") / L"Sunshine";
std::error_code ec;
std::filesystem::create_directories(path, ec);
return path;
}
inline std::filesystem::path compute_snapshot_dir() {
// When running as SYSTEM, prefer a shared ProgramData location for snapshots.
if (platf::dxgi::is_running_as_system()) {
std::wstring programDataW;
programDataW.resize(MAX_PATH);
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA, nullptr, SHGFP_TYPE_CURRENT, programDataW.data()))) {
programDataW.resize(wcslen(programDataW.c_str()));
auto path = std::filesystem::path(programDataW) / L"Sunshine";
std::error_code ec;
std::filesystem::create_directories(path, ec);
return path;
}
}
// Default to per-user roaming AppData (or fallback locations inside compute_log_dir).
return compute_log_dir();
}
struct SnapshotPaths {
std::filesystem::path golden;
std::filesystem::path golden_status;
std::filesystem::path session_current;
std::filesystem::path session_previous;
std::filesystem::path vibeshine_state;
};
inline SnapshotPaths make_snapshot_paths(const std::filesystem::path &root) {
return SnapshotPaths {
.golden = root / L"display_golden_restore.json",
.golden_status = root / L"display_golden_restore_status.json",
.session_current = root / L"display_session_current.json",
.session_previous = root / L"display_session_previous.json",
.vibeshine_state = root / L"vibeshine_state.json",
};
}
inline std::vector<std::filesystem::path> executable_config_search_roots() {
std::vector<std::filesystem::path> roots;
wchar_t exe_path[MAX_PATH] = {};
if (!GetModuleFileNameW(nullptr, exe_path, MAX_PATH)) {
return roots;
}
const auto module_path = std::filesystem::path(exe_path);
const auto module_dir = module_path.parent_path();
if (module_dir.empty()) {
return roots;
}
roots.push_back(module_dir / L"config");
roots.push_back(module_dir.parent_path() / L"config");
roots.push_back(module_dir.parent_path());
return roots;
}
inline std::vector<std::filesystem::path> snapshot_search_roots() {
std::vector<std::filesystem::path> roots;
const auto user_root = compute_log_dir();
if (!user_root.empty()) {
roots.push_back(user_root);
}
{
std::wstring programDataW;
programDataW.resize(MAX_PATH);
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA, nullptr, SHGFP_TYPE_CURRENT, programDataW.data()))) {
programDataW.resize(wcslen(programDataW.c_str()));
roots.push_back(std::filesystem::path(programDataW) / L"Sunshine");
}
}
for (const auto &root : executable_config_search_roots()) {
if (!root.empty()) {
roots.push_back(root);
}
}
// De-duplicate while preserving order.
std::vector<std::filesystem::path> uniq;
for (const auto &root : roots) {
if (root.empty()) {
continue;
}
if (std::find(uniq.begin(), uniq.end(), root) == uniq.end()) {
uniq.push_back(root);
}
}
return uniq;
}
} // namespace display_helper_paths
#endif // _WIN32