i should really work on this more
This commit is contained in:
parent
c2536c361e
commit
a8e175a5f9
9 changed files with 230 additions and 78 deletions
|
@ -1,6 +1,7 @@
|
|||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fmt/core.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "config.h"
|
||||
|
@ -11,34 +12,165 @@ namespace fs = std::filesystem;
|
|||
|
||||
namespace {
|
||||
fn GetConfigPath() -> fs::path {
|
||||
std::vector<fs::path> possiblePaths;
|
||||
|
||||
#ifdef _WIN32
|
||||
char* rawPtr = nullptr;
|
||||
size_t bufferSize = 0;
|
||||
if (_dupenv_s(&rawPtr, &bufferSize, "LOCALAPPDATA") != 0 || !rawPtr)
|
||||
throw std::runtime_error("LOCALAPPDATA env var not found");
|
||||
std::unique_ptr<char, decltype(&free)> localAppData(rawPtr, free);
|
||||
return fs::path(localAppData.get()) / "draconis++" / "config.toml";
|
||||
// Windows possible paths in order of preference
|
||||
if (auto result = GetEnv("LOCALAPPDATA"); result)
|
||||
possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml");
|
||||
|
||||
if (auto result = GetEnv("USERPROFILE"); result) {
|
||||
// Support for .config style on Windows (some users prefer this)
|
||||
possiblePaths.push_back(fs::path(*result) / ".config" / "draconis++" / "config.toml");
|
||||
// Traditional Windows location alternative
|
||||
possiblePaths.push_back(fs::path(*result) / "AppData" / "Local" / "draconis++" / "config.toml");
|
||||
}
|
||||
|
||||
if (auto result = GetEnv("APPDATA"); result)
|
||||
possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml");
|
||||
|
||||
// Portable app option - config in same directory as executable
|
||||
possiblePaths.push_back(fs::path(".") / "config.toml");
|
||||
#else
|
||||
const char* home = std::getenv("HOME");
|
||||
if (!home)
|
||||
throw std::runtime_error("HOME env var not found");
|
||||
return fs::path(home) / ".config" / "draconis++" / "config.toml";
|
||||
// Unix/Linux paths in order of preference
|
||||
if (auto result = getEnv("XDG_CONFIG_HOME"); result)
|
||||
possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml");
|
||||
|
||||
if (auto result = getEnv("HOME"); result) {
|
||||
possiblePaths.push_back(fs::path(*result) / ".config" / "draconis++" / "config.toml");
|
||||
possiblePaths.push_back(fs::path(*result) / ".draconis++" / "config.toml");
|
||||
}
|
||||
|
||||
// System-wide config
|
||||
possiblePaths.push_back(fs::path("/etc/draconis++/config.toml"));
|
||||
#endif
|
||||
|
||||
// Check if any of these configs already exist
|
||||
for (const auto& path : possiblePaths)
|
||||
if (std::error_code errc; exists(path, errc) && !errc)
|
||||
return path;
|
||||
|
||||
// If no config exists yet, return the default (first in priority)
|
||||
if (!possiblePaths.empty()) {
|
||||
// Create directory structure for the default path
|
||||
const fs::path defaultDir = possiblePaths[0].parent_path();
|
||||
|
||||
if (std::error_code errc; !exists(defaultDir, errc) && !errc) {
|
||||
create_directories(defaultDir, errc);
|
||||
if (errc)
|
||||
WARN_LOG("Warning: Failed to create config directory: {}", errc.message());
|
||||
}
|
||||
|
||||
return possiblePaths[0];
|
||||
}
|
||||
|
||||
// Ultimate fallback if somehow we have no paths
|
||||
throw std::runtime_error("Could not determine a valid config path");
|
||||
}
|
||||
|
||||
fn CreateDefaultConfig(const fs::path& configPath) -> bool {
|
||||
try {
|
||||
// Ensure the directory exists
|
||||
std::error_code errc;
|
||||
create_directories(configPath.parent_path(), errc);
|
||||
if (errc) {
|
||||
ERROR_LOG("Failed to create config directory: {}", errc.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a default TOML document
|
||||
toml::table root;
|
||||
|
||||
// Get default username for General section
|
||||
std::string defaultName;
|
||||
#ifdef _WIN32
|
||||
std::array<char, 256> username;
|
||||
DWORD size = sizeof(username);
|
||||
defaultName = GetUserNameA(username.data(), &size) ? username.data() : "User";
|
||||
#else
|
||||
if (struct passwd* pwd = getpwuid(getuid()); pwd)
|
||||
defaultName = pwd->pw_name;
|
||||
else if (const char* envUser = getenv("USER"))
|
||||
defaultName = envUser;
|
||||
else
|
||||
defaultName = "User";
|
||||
#endif
|
||||
|
||||
// General section
|
||||
toml::table* general = root.insert("general", toml::table {}).first->second.as_table();
|
||||
general->insert("name", defaultName);
|
||||
|
||||
// Now Playing section
|
||||
toml::table* nowPlaying = root.insert("now_playing", toml::table {}).first->second.as_table();
|
||||
nowPlaying->insert("enabled", false);
|
||||
|
||||
// Weather section
|
||||
toml::table* weather = root.insert("weather", toml::table {}).first->second.as_table();
|
||||
weather->insert("enabled", false);
|
||||
weather->insert("show_town_name", false);
|
||||
weather->insert("api_key", "");
|
||||
weather->insert("units", "metric");
|
||||
weather->insert("location", "London");
|
||||
|
||||
// Write to file (using a stringstream for comments + TOML)
|
||||
std::ofstream file(configPath);
|
||||
if (!file) {
|
||||
ERROR_LOG("Failed to open config file for writing: {}", configPath.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
file << "# Draconis++ Configuration File\n\n";
|
||||
|
||||
file << "# General settings\n";
|
||||
file << "[general]\n";
|
||||
file << "name = \"" << defaultName << "\" # Your display name\n\n";
|
||||
|
||||
file << "# Now Playing integration\n";
|
||||
file << "[now_playing]\n";
|
||||
file << "enabled = false # Set to true to enable media integration\n\n";
|
||||
|
||||
file << "# Weather settings\n";
|
||||
file << "[weather]\n";
|
||||
file << "enabled = false # Set to true to enable weather display\n";
|
||||
file << "show_town_name = false # Show location name in weather display\n";
|
||||
file << "api_key = \"\" # Your weather API key\n";
|
||||
file << "units = \"metric\" # Use \"metric\" for °C or \"imperial\" for °F\n";
|
||||
file << "location = \"London\" # Your city name\n\n";
|
||||
|
||||
file << "# Alternatively, you can specify coordinates instead of a city name:\n";
|
||||
file << "# [weather.location]\n";
|
||||
file << "# lat = 51.5074\n";
|
||||
file << "# lon = -0.1278\n";
|
||||
|
||||
INFO_LOG("Created default config file at {}", configPath.string());
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
ERROR_LOG("Failed to create default config file: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Config::getInstance() -> Config {
|
||||
try {
|
||||
const fs::path configPath = GetConfigPath();
|
||||
if (!fs::exists(configPath)) {
|
||||
WARN_LOG("Config file not found, using defaults");
|
||||
return Config {};
|
||||
|
||||
// Check if the config file exists
|
||||
if (!exists(configPath)) {
|
||||
INFO_LOG("Config file not found, creating defaults at {}", configPath.string());
|
||||
|
||||
// Create default config
|
||||
if (!CreateDefaultConfig(configPath)) {
|
||||
WARN_LOG("Failed to create default config, using in-memory defaults");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
auto config = toml::parse_file(configPath.string());
|
||||
return Config::fromToml(config);
|
||||
// Now we should have a config file to read
|
||||
const toml::parse_result config = toml::parse_file(configPath.string());
|
||||
return fromToml(config);
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOG("Config loading failed: {}, using defaults", e.what());
|
||||
return Config {};
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ struct General {
|
|||
static fn fromToml(const toml::table& tbl) -> General {
|
||||
General gen;
|
||||
|
||||
if (std::optional<string> name = tbl["name"].value<string>())
|
||||
if (const std::optional<string> name = tbl["name"].value<string>())
|
||||
gen.name = *name;
|
||||
|
||||
return gen;
|
||||
|
@ -65,7 +65,7 @@ struct Weather {
|
|||
weather.api_key = tbl["api_key"].value<string>().value_or("");
|
||||
weather.units = tbl["units"].value<string>().value_or("metric");
|
||||
|
||||
if (auto location = tbl["location"]) {
|
||||
if (const toml::node_view<const toml::node> location = tbl["location"]) {
|
||||
if (location.is_string()) {
|
||||
weather.location = location.value<string>().value();
|
||||
} else if (location.is_table()) {
|
||||
|
|
|
@ -13,13 +13,10 @@
|
|||
namespace fs = std::filesystem;
|
||||
using namespace std::string_literals;
|
||||
|
||||
template <typename T>
|
||||
using Result = std::expected<T, string>;
|
||||
|
||||
namespace {
|
||||
constexpr glz::opts glaze_opts = { .error_on_unknown_keys = false };
|
||||
|
||||
fn GetCachePath() -> Result<fs::path> {
|
||||
fn GetCachePath() -> std::expected<fs::path, string> {
|
||||
std::error_code errc;
|
||||
fs::path cachePath = fs::temp_directory_path(errc);
|
||||
|
||||
|
@ -30,8 +27,8 @@ namespace {
|
|||
return cachePath;
|
||||
}
|
||||
|
||||
fn ReadCacheFromFile() -> Result<WeatherOutput> {
|
||||
Result<fs::path> cachePath = GetCachePath();
|
||||
fn ReadCacheFromFile() -> std::expected<WeatherOutput, string> {
|
||||
std::expected<fs::path, string> cachePath = GetCachePath();
|
||||
|
||||
if (!cachePath)
|
||||
return std::unexpected(cachePath.error());
|
||||
|
@ -44,11 +41,10 @@ namespace {
|
|||
DEBUG_LOG("Reading from cache file...");
|
||||
|
||||
try {
|
||||
const string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
WeatherOutput result;
|
||||
glz::error_ctx errc = glz::read<glaze_opts>(result, content);
|
||||
const string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
WeatherOutput result;
|
||||
|
||||
if (errc.ec != glz::error_code::none)
|
||||
if (const glz::error_ctx errc = glz::read<glaze_opts>(result, content); errc.ec != glz::error_code::none)
|
||||
return std::unexpected("JSON parse error: " + glz::format_error(errc, content));
|
||||
|
||||
DEBUG_LOG("Successfully read from cache file.");
|
||||
|
@ -56,8 +52,8 @@ namespace {
|
|||
} catch (const std::exception& e) { return std::unexpected("Error reading cache: "s + e.what()); }
|
||||
}
|
||||
|
||||
fn WriteCacheToFile(const WeatherOutput& data) -> Result<void> {
|
||||
Result<fs::path> cachePath = GetCachePath();
|
||||
fn WriteCacheToFile(const WeatherOutput& data) -> std::expected<void, string> {
|
||||
std::expected<fs::path, string> cachePath = GetCachePath();
|
||||
|
||||
if (!cachePath)
|
||||
return std::unexpected(cachePath.error());
|
||||
|
@ -72,10 +68,9 @@ namespace {
|
|||
if (!ofs.is_open())
|
||||
return std::unexpected("Failed to open temp file: " + tempPath.string());
|
||||
|
||||
string jsonStr;
|
||||
glz::error_ctx errc = glz::write_json(data, jsonStr);
|
||||
string jsonStr;
|
||||
|
||||
if (errc.ec != glz::error_code::none)
|
||||
if (const glz::error_ctx errc = glz::write_json(data, jsonStr); errc.ec != glz::error_code::none)
|
||||
return std::unexpected("JSON serialization error: " + glz::format_error(errc, jsonStr));
|
||||
|
||||
ofs << jsonStr;
|
||||
|
@ -101,7 +96,7 @@ namespace {
|
|||
return totalSize;
|
||||
}
|
||||
|
||||
fn MakeApiRequest(const string& url) -> const Result<WeatherOutput> {
|
||||
fn MakeApiRequest(const string& url) -> std::expected<WeatherOutput, string> {
|
||||
DEBUG_LOG("Making API request to URL: {}", url);
|
||||
CURL* curl = curl_easy_init();
|
||||
string responseBuffer;
|
||||
|
@ -121,10 +116,9 @@ namespace {
|
|||
if (res != CURLE_OK)
|
||||
return std::unexpected(fmt::format("cURL error: {}", curl_easy_strerror(res)));
|
||||
|
||||
WeatherOutput output;
|
||||
glz::error_ctx errc = glz::read<glaze_opts>(output, responseBuffer);
|
||||
WeatherOutput output;
|
||||
|
||||
if (errc.ec != glz::error_code::none)
|
||||
if (const glz::error_ctx errc = glz::read<glaze_opts>(output, responseBuffer); errc.ec != glz::error_code::none)
|
||||
return std::unexpected("API response parse error: " + glz::format_error(errc, responseBuffer));
|
||||
|
||||
return std::move(output);
|
||||
|
@ -134,11 +128,11 @@ namespace {
|
|||
fn Weather::getWeatherInfo() const -> WeatherOutput {
|
||||
using namespace std::chrono;
|
||||
|
||||
if (Result<WeatherOutput> data = ReadCacheFromFile()) {
|
||||
const WeatherOutput& dataVal = *data;
|
||||
const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
||||
if (std::expected<WeatherOutput, string> data = ReadCacheFromFile()) {
|
||||
const WeatherOutput& dataVal = *data;
|
||||
|
||||
if (cacheAge < 10min) {
|
||||
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
||||
cacheAge < 10min) {
|
||||
DEBUG_LOG("Using valid cache");
|
||||
return dataVal;
|
||||
}
|
||||
|
@ -148,13 +142,13 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
|
|||
DEBUG_LOG("Cache error: {}", data.error());
|
||||
}
|
||||
|
||||
fn handleApiResult = [](const Result<WeatherOutput>& result) -> WeatherOutput {
|
||||
fn handleApiResult = [](const std::expected<WeatherOutput, string>& result) -> WeatherOutput {
|
||||
if (!result) {
|
||||
ERROR_LOG("API request failed: {}", result.error());
|
||||
return WeatherOutput {};
|
||||
}
|
||||
|
||||
if (Result<void> writeResult = WriteCacheToFile(*result); !writeResult)
|
||||
if (std::expected<void, string> writeResult = WriteCacheToFile(*result); !writeResult)
|
||||
ERROR_LOG("Failed to write cache: {}", writeResult.error());
|
||||
|
||||
return *result;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "../util/types.h"
|
||||
|
||||
// NOLINTBEGIN(readability-identifier-naming)
|
||||
|
||||
struct Condition {
|
||||
string description;
|
||||
|
||||
|
@ -40,5 +39,4 @@ struct WeatherOutput {
|
|||
static constexpr auto value = glz::object("main", &T::main, "name", &T::name, "weather", &T::weather, "dt", &T::dt);
|
||||
};
|
||||
};
|
||||
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include <expected>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <ftxui/dom/elements.hpp>
|
||||
#include <ftxui/screen/screen.hpp>
|
||||
|
@ -29,7 +28,7 @@ struct fmt::formatter<BytesToGiB> : fmt::formatter<double> {
|
|||
template <typename FmtCtx>
|
||||
constexpr fn format(const BytesToGiB& BTG, FmtCtx& ctx) const -> typename FmtCtx::iterator {
|
||||
// Format as double with GiB suffix, no space
|
||||
return fmt::format_to(ctx.out(), "{:.2f}GiB", static_cast<double>(BTG.value) / GIB);
|
||||
return fmt::format_to(ctx.out(), "{:.2f}GiB", static_cast<f64>(BTG.value) / GIB);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -265,9 +264,8 @@ namespace {
|
|||
|
||||
// Now Playing row
|
||||
if (nowPlayingEnabled && data.now_playing.has_value()) {
|
||||
const std::expected<string, NowPlayingError>& nowPlayingResult = *data.now_playing;
|
||||
|
||||
if (nowPlayingResult.has_value()) {
|
||||
if (const std::expected<string, NowPlayingError>& nowPlayingResult = *data.now_playing;
|
||||
nowPlayingResult.has_value()) {
|
||||
const std::string& npText = *nowPlayingResult;
|
||||
|
||||
content.push_back(separator() | color(borderColor));
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <ranges>
|
||||
#ifdef _WIN32
|
||||
|
||||
// clang-format off
|
||||
|
@ -14,6 +15,8 @@
|
|||
#include <guiddef.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Media.Control.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.System.Diagnostics.h>
|
||||
#include <winrt/base.h>
|
||||
#include <winrt/impl/Windows.Media.Control.2.h>
|
||||
|
||||
|
@ -22,7 +25,7 @@
|
|||
using std::string_view;
|
||||
using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW);
|
||||
|
||||
// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
||||
// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr,*-pro-type-reinterpret-cast)
|
||||
namespace {
|
||||
class ProcessSnapshot {
|
||||
public:
|
||||
|
@ -55,11 +58,11 @@ namespace {
|
|||
// Get first process
|
||||
if (Process32First(h_snapshot, &pe32)) {
|
||||
// Add first process to vector
|
||||
processes.emplace_back(pe32.th32ProcessID, string(static_cast<const char*>(pe32.szExeFile)));
|
||||
processes.emplace_back(pe32.th32ProcessID, string(reinterpret_cast<const char*>(pe32.szExeFile)));
|
||||
|
||||
// Add remaining processes
|
||||
while (Process32Next(h_snapshot, &pe32))
|
||||
processes.emplace_back(pe32.th32ProcessID, string(static_cast<const char*>(pe32.szExeFile)));
|
||||
processes.emplace_back(pe32.th32ProcessID, string(reinterpret_cast<const char*>(pe32.szExeFile)));
|
||||
}
|
||||
|
||||
return processes;
|
||||
|
@ -94,7 +97,7 @@ namespace {
|
|||
}
|
||||
|
||||
fn GetProcessInfo() -> std::vector<std::pair<DWORD, string>> {
|
||||
ProcessSnapshot snapshot;
|
||||
const ProcessSnapshot snapshot;
|
||||
return snapshot.isValid() ? snapshot.getProcesses() : std::vector<std::pair<DWORD, string>> {};
|
||||
}
|
||||
|
||||
|
@ -104,8 +107,8 @@ namespace {
|
|||
});
|
||||
}
|
||||
|
||||
fn GetParentProcessId(DWORD pid) -> DWORD {
|
||||
ProcessSnapshot snapshot;
|
||||
fn GetParentProcessId(const DWORD pid) -> DWORD {
|
||||
const ProcessSnapshot snapshot;
|
||||
if (!snapshot.isValid())
|
||||
return 0;
|
||||
|
||||
|
@ -126,7 +129,7 @@ namespace {
|
|||
}
|
||||
|
||||
fn GetProcessName(const DWORD pid) -> string {
|
||||
ProcessSnapshot snapshot;
|
||||
const ProcessSnapshot snapshot;
|
||||
if (!snapshot.isValid())
|
||||
return "";
|
||||
|
||||
|
@ -137,24 +140,24 @@ namespace {
|
|||
return "";
|
||||
|
||||
if (pe32.th32ProcessID == pid)
|
||||
return { static_cast<const char*>(pe32.szExeFile) };
|
||||
return reinterpret_cast<const char*>(pe32.szExeFile);
|
||||
|
||||
while (Process32Next(snapshot.h_snapshot, &pe32))
|
||||
if (pe32.th32ProcessID == pid)
|
||||
return { static_cast<const char*>(pe32.szExeFile) };
|
||||
return reinterpret_cast<const char*>(pe32.szExeFile);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
fn GetMemInfo() -> expected<u64, string> {
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||
|
||||
if (!GlobalMemoryStatusEx(&memInfo))
|
||||
return std::unexpected("Failed to get memory status");
|
||||
|
||||
return memInfo.ullTotalPhys;
|
||||
try {
|
||||
using namespace winrt::Windows::System::Diagnostics;
|
||||
const SystemDiagnosticInfo diag = SystemDiagnosticInfo::GetForCurrentSystem();
|
||||
return diag.MemoryUsage().GetReport().TotalPhysicalSizeInBytes();
|
||||
} catch (const winrt::hresult_error& e) {
|
||||
return std::unexpected("Failed to get memory info: " + to_string(e.message()));
|
||||
}
|
||||
}
|
||||
|
||||
fn GetNowPlaying() -> expected<string, NowPlayingError> {
|
||||
|
@ -185,11 +188,11 @@ fn GetNowPlaying() -> expected<string, NowPlayingError> {
|
|||
|
||||
fn GetOSVersion() -> expected<string, string> {
|
||||
// First try using the native Windows API
|
||||
OSVERSIONINFOEXW osvi = { sizeof(OSVERSIONINFOEXW), 0, 0, 0, 0, { 0 }, 0, 0, 0, 0, 0 };
|
||||
NTSTATUS status = 0;
|
||||
constexpr OSVERSIONINFOEXW osvi = { sizeof(OSVERSIONINFOEXW), 0, 0, 0, 0, { 0 }, 0, 0, 0, 0, 0 };
|
||||
NTSTATUS status = 0;
|
||||
|
||||
// Get RtlGetVersion function from ntdll.dll (not affected by application manifest)
|
||||
if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll"))
|
||||
if (const HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll"))
|
||||
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion")))
|
||||
status = rtlGetVersion(std::bit_cast<PRTL_OSVERSIONINFOW>(&osvi));
|
||||
|
||||
|
@ -290,7 +293,7 @@ fn GetWindowManager() -> string {
|
|||
std::vector<string> processNames;
|
||||
|
||||
processNames.reserve(processInfo.size());
|
||||
for (const auto& [pid, name] : processInfo) processNames.push_back(name);
|
||||
for (const auto& name : processInfo | std::views::values) processNames.push_back(name);
|
||||
|
||||
// Check for third-party WMs using a map for cleaner code
|
||||
const std::unordered_map<string, string> wmProcesses = {
|
||||
|
@ -382,7 +385,6 @@ fn GetShell() -> string {
|
|||
size_t shellLen = 0;
|
||||
_dupenv_s(&shell, &shellLen, "SHELL");
|
||||
const std::unique_ptr<char, decltype(&free)> shellGuard(shell, free);
|
||||
string shellExe;
|
||||
|
||||
// If SHELL is empty, try LOGINSHELL
|
||||
if (!shell || strlen(shell) == 0) {
|
||||
|
@ -394,6 +396,7 @@ fn GetShell() -> string {
|
|||
}
|
||||
|
||||
if (shell) {
|
||||
string shellExe;
|
||||
const string shellPath = shell;
|
||||
const size_t lastSlash = shellPath.find_last_of("\\/");
|
||||
shellExe = (lastSlash != string::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
|
||||
|
@ -463,5 +466,6 @@ fn GetDiskUsage() -> std::pair<u64, u64> {
|
|||
|
||||
return { 0, 0 };
|
||||
}
|
||||
// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
||||
// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr,*-pro-type-reinterpret-cast)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
// probably stupid but it fixes the issue with windows.h defining ERROR
|
||||
#include <utility>
|
||||
#undef ERROR
|
||||
|
||||
#include <filesystem>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <expected>
|
||||
// ReSharper disable once CppUnusedIncludeDirective
|
||||
#include <guiddef.h>
|
||||
#include <variant>
|
||||
#include <winrt/base.h>
|
||||
|
@ -149,13 +151,13 @@ enum class NowPlayingCode : u8 {
|
|||
* @brief Represents a Linux-specific error.
|
||||
*/
|
||||
using LinuxError = std::string;
|
||||
#elifdef __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
/**
|
||||
* @typedef MacError
|
||||
* @brief Represents a macOS-specific error.
|
||||
*/
|
||||
using MacError = std::string;
|
||||
#elifdef _WIN32
|
||||
#elif defined(_WIN32)
|
||||
/**
|
||||
* @typedef WindowsError
|
||||
* @brief Represents a Windows-specific error.
|
||||
|
@ -168,9 +170,34 @@ using NowPlayingError = std::variant<
|
|||
NowPlayingCode,
|
||||
#ifdef __linux__
|
||||
LinuxError
|
||||
#elifdef __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
MacError
|
||||
#elifdef _WIN32
|
||||
#elif defined(_WIN32)
|
||||
WindowsError
|
||||
#endif
|
||||
>;
|
||||
|
||||
enum class EnvError : u8 { NotFound, AccessError };
|
||||
|
||||
inline auto GetEnv(const std::string& name) -> std::expected<std::string, EnvError> {
|
||||
#ifdef _WIN32
|
||||
char* rawPtr = nullptr;
|
||||
size_t bufferSize = 0;
|
||||
|
||||
if (_dupenv_s(&rawPtr, &bufferSize, name.c_str()) != 0)
|
||||
return std::unexpected(EnvError::AccessError);
|
||||
|
||||
if (!rawPtr)
|
||||
return std::unexpected(EnvError::NotFound);
|
||||
|
||||
const std::string result(rawPtr);
|
||||
free(rawPtr);
|
||||
return result;
|
||||
#else
|
||||
const char* value = std::getenv(name.c_str());
|
||||
if (!value)
|
||||
return std::unexpected(EnvError::NotFound);
|
||||
|
||||
return std::string(value);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit cfa3c5838cc04e2f179faddf8e4757f90fa5fbe7
|
||||
Subproject commit bad0345d6358a649d5f72e90ada2be75d04b75cd
|
Loading…
Add table
Reference in a new issue