um i think this owrks

This commit is contained in:
Mars 2025-04-23 21:47:25 -04:00
parent cf51e3e569
commit 2219182539
9 changed files with 306 additions and 334 deletions

View file

@ -1,8 +1,11 @@
AlignAfterOpenBracket: BlockIndent AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: Right AlignArrayOfStructures: Right
AlignConsecutiveAssignments: true AlignConsecutiveAssignments: true
AlignConsecutiveShortCaseStatements:
Enabled: true
AlignConsecutiveDeclarations: true AlignConsecutiveDeclarations: true
AllowShortBlocksOnASingleLine: Always AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: All
AllowShortLoopsOnASingleLine: true AllowShortLoopsOnASingleLine: true

View file

@ -7,9 +7,9 @@ project(
version : '0.1.0', version : '0.1.0',
default_options : [ default_options : [
'default_library=static', 'default_library=static',
'warning_level=everything',
'buildtype=debugoptimized', 'buildtype=debugoptimized',
'b_vscrt=mt', 'b_vscrt=mt',
'warning_level=3',
], ],
) )
@ -43,7 +43,12 @@ common_cpp_flags = {
'/MT', '/MT',
'/Zc:__cplusplus', '/Zc:__cplusplus',
'/Zc:preprocessor', '/Zc:preprocessor',
'/external:W0',
'/external:anglebrackets',
'/std:c++latest', '/std:c++latest',
'/w44668',
'/w44710',
'/w44820',
], ],
'unix_extra' : [ 'unix_extra' : [
'-march=native', '-march=native',

View file

@ -1,6 +1,5 @@
#include <cstdlib> #include <cstdlib>
#include <filesystem> #include <filesystem>
#include <iostream>
#include <stdexcept> #include <stdexcept>
#include "config.h" #include "config.h"
@ -149,6 +148,8 @@ fn Config::getInstance() -> Config {
} }
const toml::parse_result config = toml::parse_file(configPath.string()); const toml::parse_result config = toml::parse_file(configPath.string());
DEBUG_LOG("Config loaded from {}", configPath.string());
return fromToml(config); return fromToml(config);
} catch (const std::exception& e) { } catch (const std::exception& e) {
DEBUG_LOG("Config loading failed: {}, using defaults", e.what()); DEBUG_LOG("Config loading failed: {}, using defaults", e.what());

View file

@ -146,19 +146,19 @@ namespace {
// Single-threaded execution for core system info (faster on Windows) // Single-threaded execution for core system info (faster on Windows)
data.date = GetDate(); data.date = GetDate();
data.host = GetHost(); data.host = os::GetHost();
data.kernel_version = GetKernelVersion(); data.kernel_version = os::GetKernelVersion();
data.os_version = GetOSVersion(); data.os_version = os::GetOSVersion();
data.mem_info = GetMemInfo(); data.mem_info = os::GetMemInfo();
// Desktop environment info // Desktop environment info
data.desktop_environment = GetDesktopEnvironment(); data.desktop_environment = os::GetDesktopEnvironment();
data.window_manager = GetWindowManager(); data.window_manager = os::GetWindowManager();
// Parallel execution for disk/shell only // Parallel execution for disk/shell only
auto diskShell = std::async(std::launch::async, [] { auto diskShell = std::async(std::launch::async, [] {
auto [used, total] = GetDiskUsage(); auto [used, total] = os::GetDiskUsage();
return std::make_tuple(used, total, GetShell()); return std::make_tuple(used, total, os::GetShell());
}); });
// Conditional tasks // Conditional tasks
@ -169,7 +169,7 @@ namespace {
weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); }); weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); });
if (config.now_playing.enabled) if (config.now_playing.enabled)
nowPlaying = std::async(std::launch::async, GetNowPlaying); nowPlaying = std::async(std::launch::async, os::GetNowPlaying);
// Get remaining results // Get remaining results
auto [used, total, shell] = diskShell.get(); auto [used, total, shell] = diskShell.get();
@ -330,17 +330,8 @@ namespace {
if (std::holds_alternative<NowPlayingCode>(error)) if (std::holds_alternative<NowPlayingCode>(error))
switch (std::get<NowPlayingCode>(error)) { switch (std::get<NowPlayingCode>(error)) {
case NowPlayingCode::NoPlayers: case NowPlayingCode::NoPlayers: DEBUG_LOG("No players found"); break;
DEBUG_LOG("No players found"); case NowPlayingCode::NoActivePlayer: DEBUG_LOG("No active player found"); break;
break;
case NowPlayingCode::NoActivePlayer:
DEBUG_LOG("No active player found");
break;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
default:
std::unreachable();
#pragma clang diagnostic pop
} }
#ifdef _WIN32 #ifdef _WIN32

View file

@ -3,6 +3,7 @@
#include "../util/macros.h" #include "../util/macros.h"
#include "../util/types.h" #include "../util/types.h"
namespace os {
/** /**
* @brief Get the amount of installed RAM in bytes. * @brief Get the amount of installed RAM in bytes.
*/ */
@ -53,3 +54,4 @@ fn GetPackageCount() -> u64;
* @return std::pair<u64, u64> Used space/total space * @return std::pair<u64, u64> Used space/total space
*/ */
fn GetDiskUsage() -> Pair<u64, u64>; fn GetDiskUsage() -> Pair<u64, u64>;
}

View file

@ -9,20 +9,57 @@
// clang-format on // clang-format on
#include <cstring> #include <cstring>
#include <guiddef.h> #include <ranges>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Media.Control.h> #include <winrt/Windows.Media.Control.h>
#include <winrt/Windows.Storage.h> #include <winrt/Windows.Storage.h>
#include <winrt/Windows.System.Diagnostics.h> #include <winrt/Windows.System.Diagnostics.h>
#include <winrt/Windows.System.Profile.h>
#include <winrt/base.h> #include <winrt/base.h>
#include <winrt/impl/Windows.Media.Control.2.h> #include <winrt/impl/Windows.Media.Control.2.h>
#include "os.h" #include "os.h"
using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW);
// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr,*-pro-type-reinterpret-cast)
namespace { namespace {
struct OSVersion {
u16 major;
u16 minor;
u16 build;
u16 revision;
static fn parseDeviceFamilyVersion(const winrt::hstring& versionString) -> OSVersion {
try {
const u64 versionUl = std::stoull(winrt::to_string(versionString));
return {
.major = static_cast<u16>((versionUl >> 48) & 0xFFFF),
.minor = static_cast<u16>((versionUl >> 32) & 0xFFFF),
.build = static_cast<u16>((versionUl >> 16) & 0xFFFF),
.revision = static_cast<u16>(versionUl & 0xFFFF),
};
} catch (const std::invalid_argument& e) {
ERROR_LOG("Invalid argument: {}", e.what());
} catch (const std::out_of_range& e) {
ERROR_LOG("Value out of range: {}", e.what());
} catch (const winrt::hresult_error& e) { ERROR_LOG("Windows error: {}", winrt::to_string(e.message())); }
return { .major = 0, .minor = 0, .build = 0, .revision = 0 };
}
};
// clang-format off
constexpr Array<Pair<StringView, StringView>, 3> windowsShellMap = {{
{ "cmd", "Command Prompt" },
{ "powershell", "PowerShell" },
{ "pwsh", "PowerShell Core" },
}};
constexpr Array<Pair<StringView, StringView>, 3> msysShellMap = {{
{ "bash", "Bash" },
{ "zsh", "Zsh" },
{ "fish", "Fish" },
}};
// clang-format on
class ProcessSnapshot { class ProcessSnapshot {
public: public:
ProcessSnapshot() : h_snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) {} ProcessSnapshot() : h_snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) {}
@ -39,8 +76,8 @@ namespace {
[[nodiscard]] fn isValid() const -> bool { return h_snapshot != INVALID_HANDLE_VALUE; } [[nodiscard]] fn isValid() const -> bool { return h_snapshot != INVALID_HANDLE_VALUE; }
[[nodiscard]] fn getProcesses() const -> std::vector<std::pair<DWORD, String>> { [[nodiscard]] fn getProcesses() const -> Vec<Pair<DWORD, String>> {
std::vector<std::pair<DWORD, String>> processes; Vec<Pair<DWORD, String>> processes;
if (!isValid()) if (!isValid())
return processes; return processes;
@ -51,12 +88,9 @@ namespace {
if (!Process32First(h_snapshot, &pe32)) if (!Process32First(h_snapshot, &pe32))
return processes; return processes;
// Get first process
if (Process32First(h_snapshot, &pe32)) { if (Process32First(h_snapshot, &pe32)) {
// Add first process to vector
processes.emplace_back(pe32.th32ProcessID, String(reinterpret_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)) while (Process32Next(h_snapshot, &pe32))
processes.emplace_back(pe32.th32ProcessID, String(reinterpret_cast<const char*>(pe32.szExeFile))); processes.emplace_back(pe32.th32ProcessID, String(reinterpret_cast<const char*>(pe32.szExeFile)));
} }
@ -79,10 +113,9 @@ namespace {
return ""; return "";
} }
// For string values, allocate one less byte to avoid the null terminator
String value((type == REG_SZ || type == REG_EXPAND_SZ) ? dataSize - 1 : dataSize, '\0'); String value((type == REG_SZ || type == REG_EXPAND_SZ) ? dataSize - 1 : dataSize, '\0');
if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, std::bit_cast<LPBYTE>(value.data()), &dataSize) != if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(value.data()), &dataSize) !=
ERROR_SUCCESS) { ERROR_SUCCESS) {
RegCloseKey(key); RegCloseKey(key);
return ""; return "";
@ -92,12 +125,12 @@ namespace {
return value; return value;
} }
fn GetProcessInfo() -> std::vector<std::pair<DWORD, String>> { fn GetProcessInfo() -> Vec<Pair<DWORD, String>> {
const ProcessSnapshot snapshot; const ProcessSnapshot snapshot;
return snapshot.isValid() ? snapshot.getProcesses() : std::vector<std::pair<DWORD, String>> {}; return snapshot.isValid() ? snapshot.getProcesses() : std::vector<std::pair<DWORD, String>> {};
} }
fn IsProcessRunning(const std::vector<String>& processes, const String& name) -> bool { fn IsProcessRunning(const Vec<String>& processes, const String& name) -> bool {
return std::ranges::any_of(processes, [&name](const String& proc) -> bool { return std::ranges::any_of(processes, [&name](const String& proc) -> bool {
return _stricmp(proc.c_str(), name.c_str()) == 0; return _stricmp(proc.c_str(), name.c_str()) == 0;
}); });
@ -143,139 +176,130 @@ namespace {
return ""; return "";
} }
template <usize sz>
fn FindShellInProcessTree(const DWORD startPid, const Array<Pair<StringView, StringView>, sz>& shellMap)
-> std::optional<String> {
DWORD pid = startPid;
while (pid != 0) {
String processName = GetProcessName(pid);
if (processName.empty()) {
pid = GetParentProcessId(pid);
continue;
} }
fn GetMemInfo() -> Result<u64, String> { std::ranges::transform(processName, processName.begin(), [](const u8 character) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(character)));
});
if (processName.length() > 4 && processName.substr(processName.length() - 4) == ".exe")
processName.resize(processName.length() - 4);
auto iter = std::ranges::find_if(shellMap, [&](const auto& pair) {
return std::string_view { processName } == pair.first;
});
if (iter != std::ranges::end(shellMap))
return String { iter->second };
pid = GetParentProcessId(pid);
}
return std::nullopt;
}
fn GetBuildNumber() -> Option<u64> {
try { try {
using namespace winrt::Windows::System::Diagnostics; using namespace winrt::Windows::System::Profile;
const SystemDiagnosticInfo diag = SystemDiagnosticInfo::GetForCurrentSystem(); const auto versionInfo = AnalyticsInfo::VersionInfo();
return diag.MemoryUsage().GetReport().TotalPhysicalSizeInBytes(); const winrt::hstring familyVersion = versionInfo.DeviceFamilyVersion();
if (!familyVersion.empty()) {
const u64 versionUl = std::stoull(winrt::to_string(familyVersion));
return (versionUl >> 16) & 0xFFFF;
}
} catch (const winrt::hresult_error& e) {
DEBUG_LOG("WinRT error getting build number: {}", winrt::to_string(e.message()));
} catch (const Exception& e) { DEBUG_LOG("Standard exception getting build number: {}", e.what()); }
return None;
}
}
fn os::GetMemInfo() -> Result<u64, String> {
try {
return winrt::Windows::System::Diagnostics::SystemDiagnosticInfo::GetForCurrentSystem()
.MemoryUsage()
.GetReport()
.TotalPhysicalSizeInBytes();
} catch (const winrt::hresult_error& e) { } catch (const winrt::hresult_error& e) {
return Err(std::format("Failed to get memory info: {}", to_string(e.message()))); return Err(std::format("Failed to get memory info: {}", to_string(e.message())));
} }
} }
fn GetNowPlaying() -> Result<String, NowPlayingError> { fn os::GetNowPlaying() -> Result<String, NowPlayingError> {
using namespace winrt::Windows::Media::Control; using namespace winrt::Windows::Media::Control;
using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation;
using MediaProperties = GlobalSystemMediaTransportControlsSessionMediaProperties;
using Session = GlobalSystemMediaTransportControlsSession; using Session = GlobalSystemMediaTransportControlsSession;
using SessionManager = GlobalSystemMediaTransportControlsSessionManager; using SessionManager = GlobalSystemMediaTransportControlsSessionManager;
try { try {
// Request the session manager asynchronously
const IAsyncOperation<SessionManager> sessionManagerOp = SessionManager::RequestAsync(); const IAsyncOperation<SessionManager> sessionManagerOp = SessionManager::RequestAsync();
const SessionManager sessionManager = sessionManagerOp.get(); const SessionManager sessionManager = sessionManagerOp.get();
if (const Session currentSession = sessionManager.GetCurrentSession()) { if (const Session currentSession = sessionManager.GetCurrentSession())
// Try to get the media properties asynchronously return winrt::to_string(currentSession.TryGetMediaPropertiesAsync().get().Title());
const MediaProperties mediaProperties = currentSession.TryGetMediaPropertiesAsync().get();
// Convert the hstring title to string
return to_string(mediaProperties.Title());
}
// If we reach this point, there is no current session
return Err(NowPlayingCode::NoActivePlayer); return Err(NowPlayingCode::NoActivePlayer);
} catch (const winrt::hresult_error& e) { return Err(e); } } catch (const winrt::hresult_error& e) { return Err(e); }
} }
fn GetOSVersion() -> Result<String, String> { fn os::GetOSVersion() -> Result<String, String> {
constexpr OSVERSIONINFOEXW osvi = { sizeof(OSVERSIONINFOEXW), 0, 0, 0, 0, { 0 }, 0, 0, 0, 0, 0 }; try {
NTSTATUS status = 0; const String regSubKey = R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)";
if (const HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) String productName = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "ProductName");
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) const String displayVersion = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "DisplayVersion");
status = rtlGetVersion(std::bit_cast<PRTL_OSVERSIONINFOW>(&osvi));
String productName; if (productName.empty())
String edition; return Err("Failed to read ProductName");
if (status == 0) { if (const Option<u64> buildNumber = GetBuildNumber()) {
DWORD productType = 0; if (*buildNumber >= 22000)
if (GetProductInfo( if (const usize pos = productName.find("Windows 10");
osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &productType pos != String::npos && (pos == 0 || !isalnum(static_cast<u8>(productName[pos - 1]))) &&
)) { (pos + 10 == productName.length() || !isalnum(static_cast<u8>(productName[pos + 10]))))
if (osvi.dwMajorVersion == 10) { productName.replace(pos, 10, "Windows 11");
if (osvi.dwBuildNumber >= 22000) } else
productName = "Windows 11"; DEBUG_LOG("Warning: Could not get build number via WinRT; Win11 patch relies on registry ProductName only.");
else
productName = "Windows 10";
switch (productType) { return displayVersion.empty() ? productName : productName + " " + displayVersion;
case PRODUCT_PROFESSIONAL: } catch (const Exception& e) { return Err(std::format("Exception occurred getting OS version: {}", e.what())); }
edition = " Pro";
break;
case PRODUCT_ENTERPRISE:
edition = " Enterprise";
break;
case PRODUCT_EDUCATION:
edition = " Education";
break;
case PRODUCT_HOME_BASIC:
case PRODUCT_HOME_PREMIUM:
edition = " Home";
break;
case PRODUCT_CLOUDEDITION:
edition = " Cloud";
break;
default:
break;
}
}
}
} else {
productName =
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "ProductName");
if (const i32 buildNumber = stoi(
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber")
);
buildNumber >= 22000 && productName.find("Windows 10") != String::npos)
productName.replace(productName.find("Windows 10"), 10, "Windows 11");
} }
if (!productName.empty()) { fn os::GetHost() -> String {
String result = productName + edition; return GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily");
const String displayVersion =
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "DisplayVersion");
if (!displayVersion.empty())
result += " " + displayVersion;
return result;
} }
return "Windows"; fn os::GetKernelVersion() -> String {
} try {
using namespace winrt::Windows::System::Profile;
fn GetHost() -> String { const AnalyticsVersionInfo versionInfo = AnalyticsInfo::VersionInfo();
String hostName = GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily");
return hostName; if (const winrt::hstring familyVersion = versionInfo.DeviceFamilyVersion(); !familyVersion.empty())
} if (auto [major, minor, build, revision] = OSVersion::parseDeviceFamilyVersion(familyVersion); build > 0)
return std::format("{}.{}.{}.{}", major, minor, build, revision);
fn GetKernelVersion() -> String { } catch (const winrt::hresult_error& e) {
// ReSharper disable once CppLocalVariableMayBeConst ERROR_LOG("WinRT error: {}", winrt::to_string(e.message()));
if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) { } catch (const Exception& e) { ERROR_LOG("Failed to get kernel version: {}", e.what()); }
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) {
RTL_OSVERSIONINFOW osInfo = {};
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
if (rtlGetVersion(&osInfo) == 0) {
return std::format(
"{}.{}.{}.{}", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.dwPlatformId
);
}
}
}
return ""; return "";
} }
fn GetWindowManager() -> String { fn os::GetWindowManager() -> String {
const auto processInfo = GetProcessInfo(); const auto processInfo = GetProcessInfo();
std::vector<String> processNames; std::vector<String> processNames;
@ -300,7 +324,7 @@ fn GetWindowManager() -> String {
return "Windows Manager"; return "Windows Manager";
} }
fn GetDesktopEnvironment() -> Option<String> { fn os::GetDesktopEnvironment() -> Option<String> {
const String buildStr = const String buildStr =
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber"); GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber");
@ -347,97 +371,45 @@ fn GetDesktopEnvironment() -> Option<String> {
} }
} }
fn GetShell() -> String { fn os::GetShell() -> String {
// TODO: update this to use GetEnv const DWORD currentPid = GetCurrentProcessId();
const std::unordered_map<String, String> knownShells = { if (const Result<String, EnvError> msystemResult = GetEnv("MSYSTEM")) {
{ "cmd.exe", "Command Prompt" }, String shellPath;
{ "powershell.exe", "PowerShell" }, if (const Result<String, EnvError> shellResult = GetEnv("SHELL"); !shellResult->empty())
{ "pwsh.exe", "PowerShell Core" }, shellPath = *shellResult;
{ "windowsterminal.exe", "Windows Terminal" }, else if (const Result<String, EnvError> loginShellResult = GetEnv("LOGINSHELL"); !loginShellResult->empty())
{ "mintty.exe", "Mintty" }, shellPath = *loginShellResult;
{ "bash.exe", "Windows Subsystem for Linux" }
};
char* msystemEnv = nullptr; if (!shellPath.empty()) {
if (_dupenv_s(&msystemEnv, nullptr, "MSYSTEM") == 0 && msystemEnv != nullptr) { const usize lastSlash = shellPath.find_last_of("\\/");
const std::unique_ptr<char, decltype(&free)> msystemEnvGuard(msystemEnv, free); String shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
char* shell = nullptr; std::ranges::transform(shellExe, shellExe.begin(), [](const u8 c) { return std::tolower(c); });
size_t shellLen = 0;
_dupenv_s(&shell, &shellLen, "SHELL");
const std::unique_ptr<char, decltype(&free)> shellGuard(shell, free);
if (!shell || strlen(shell) == 0) { if (shellExe.ends_with(".exe"))
char* loginShell = nullptr; shellExe.resize(shellExe.length() - 4);
size_t loginShellLen = 0;
_dupenv_s(&loginShell, &loginShellLen, "LOGINSHELL"); const auto iter =
const std::unique_ptr<char, decltype(&free)> loginShellGuard(loginShell, free); std::ranges::find_if(msysShellMap, [&](const auto& pair) { return StringView { shellExe } == pair.first; });
shell = loginShell;
if (iter != std::ranges::end(msysShellMap))
return String { iter->second };
} }
if (shell) { if (const Option<String> msysShell = FindShellInProcessTree(currentPid, msysShellMap))
String shellExe; return *msysShell;
const String shellPath = shell;
const size_t lastSlash = shellPath.find_last_of("\\/");
shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
std::ranges::transform(shellExe, shellExe.begin(), ::tolower);
// Use a map for shell name lookup instead of multiple if statements return "MSYS2 Environment";
const std::unordered_map<StringView, String> shellNames = {
{ "bash", "Bash" },
{ "zsh", "Zsh" },
{ "fish", "Fish" }
};
for (const auto& [pattern, name] : shellNames) {
if (shellExe.find(pattern) != String::npos)
return name;
} }
return shellExe.empty() ? "MSYS2" : "MSYS2/" + shellExe; if (const Option<String> windowsShell = FindShellInProcessTree(currentPid, windowsShellMap))
return *windowsShell;
return "Unknown Shell";
} }
const auto processInfo = GetProcessInfo(); fn os::GetDiskUsage() -> Pair<u64, u64> {
DWORD pid = GetCurrentProcessId();
while (pid != 0) {
String processName = GetProcessName(pid);
std::ranges::transform(processName, processName.begin(), ::tolower);
const std::unordered_map<String, String> msysShells = {
{ "bash.exe", "Bash" },
{ "zsh.exe", "Zsh" },
{ "fish.exe", "Fish" },
{ "mintty.exe", "Mintty" }
};
for (const auto& [msysShellExe, shellName] : msysShells) {
if (processName == msysShellExe)
return shellName;
}
pid = GetParentProcessId(pid);
}
return "MSYS2";
}
DWORD pid = GetCurrentProcessId();
while (pid != 0) {
String processName = GetProcessName(pid);
std::ranges::transform(processName, processName.begin(), ::tolower);
if (auto shellIterator = knownShells.find(processName); shellIterator != knownShells.end())
return shellIterator->second;
pid = GetParentProcessId(pid);
}
return "Windows Console";
}
fn GetDiskUsage() -> std::pair<u64, u64> {
ULARGE_INTEGER freeBytes, totalBytes; ULARGE_INTEGER freeBytes, totalBytes;
if (GetDiskFreeSpaceExW(L"C:\\", nullptr, &totalBytes, &freeBytes)) if (GetDiskFreeSpaceExW(L"C:\\", nullptr, &totalBytes, &freeBytes))
@ -445,6 +417,5 @@ fn GetDiskUsage() -> std::pair<u64, u64> {
return { 0, 0 }; return { 0, 0 };
} }
// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr,*-pro-type-reinterpret-cast)
#endif #endif

View file

@ -17,9 +17,10 @@
#ifdef None #ifdef None
#undef None #undef None
#define None std::nullopt
#endif #endif
#define None std::nullopt
namespace term { namespace term {
enum class Emphasis : u8 { none = 0, bold = 1, italic = 2 }; enum class Emphasis : u8 { none = 0, bold = 1, italic = 2 };
@ -49,16 +50,14 @@ namespace term {
struct FgColor { struct FgColor {
Color col; Color col;
constexpr explicit FgColor(Color color) : col(color) {} constexpr explicit FgColor(const Color color) : col(color) {}
[[nodiscard]] fn ansiCode() const -> String { return std::format("\033[{}m", static_cast<int>(col)); } [[nodiscard]] fn ansiCode() const -> String { return std::format("\033[{}m", static_cast<int>(col)); }
}; };
constexpr fn Fg(Color color) -> FgColor { return FgColor(color); }
struct Style { struct Style {
Emphasis emph = Emphasis::none; Emphasis emph = Emphasis::none;
FgColor fg_col = FgColor(static_cast<Color>(-1)); // Invalid color FgColor fg_col = FgColor(static_cast<Color>(-1));
[[nodiscard]] fn ansiCode() const -> String { [[nodiscard]] fn ansiCode() const -> String {
String result; String result;
@ -77,8 +76,13 @@ namespace term {
} }
}; };
constexpr fn operator|(Emphasis emph, FgColor fgColor)->Style { return { .emph = emph, .fg_col = fgColor }; } constexpr fn operator|(const Emphasis emph, const FgColor fgColor)->Style {
constexpr fn operator|(FgColor fgColor, Emphasis emph)->Style { return { .emph = emph, .fg_col = fgColor }; } return { .emph = emph, .fg_col = fgColor };
}
constexpr fn operator|(const FgColor fgColor, const Emphasis emph)->Style {
return { .emph = emph, .fg_col = fgColor };
}
constexpr CStr reset = "\033[0m"; constexpr CStr reset = "\033[0m";
@ -122,19 +126,11 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st
const auto [color, levelStr] = [&] { const auto [color, levelStr] = [&] {
switch (level) { switch (level) {
case LogLevel::DEBUG: case LogLevel::DEBUG: return std::make_pair(log_colors::debug, "DEBUG");
return std::make_pair(log_colors::debug, "DEBUG"); case LogLevel::INFO: return std::make_pair(log_colors::info, "INFO ");
case LogLevel::INFO: case LogLevel::WARN: return std::make_pair(log_colors::warn, "WARN ");
return std::make_pair(log_colors::info, "INFO "); case LogLevel::ERROR: return std::make_pair(log_colors::error, "ERROR");
case LogLevel::WARN: default: std::unreachable();
return std::make_pair(log_colors::warn, "WARN ");
case LogLevel::ERROR:
return std::make_pair(log_colors::error, "ERROR");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
default:
std::unreachable();
#pragma clang diagnostic pop
} }
}(); }();
@ -142,20 +138,22 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st
using namespace term; using namespace term;
Print(Fg(log_colors::timestamp), "[{:%H:%M:%S}] ", now); Print(FgColor(log_colors::timestamp), "[{:%H:%M:%S}] ", now);
Print(Emphasis::bold | Fg(color), "{} ", levelStr); Print(Emphasis::bold | FgColor(color), "{} ", levelStr);
Print(fmt, std::forward<Args>(args)...); Print(fmt, std::forward<Args>(args)...);
#ifndef NDEBUG #ifndef NDEBUG
Print(Fg(log_colors::file_info), "\n{:>14} ", "╰──"); Print(FgColor(log_colors::file_info), "\n{:>14} ", "╰──");
Print(Emphasis::italic | Fg(log_colors::file_info), "{}:{}", filename, loc.line()); Print(Emphasis::italic | FgColor(log_colors::file_info), "{}:{}", filename, loc.line());
#endif #endif
Print("\n"); Print("\n");
} }
#ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-macros" #pragma clang diagnostic ignored "-Wunused-macros"
#endif
#ifdef NDEBUG #ifdef NDEBUG
#define DEBUG_LOG(...) static_cast<void>(0) #define DEBUG_LOG(...) static_cast<void>(0)
#else #else
@ -164,4 +162,6 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st
#define INFO_LOG(...) LogImpl(LogLevel::INFO, std::source_location::current(), __VA_ARGS__) #define INFO_LOG(...) LogImpl(LogLevel::INFO, std::source_location::current(), __VA_ARGS__)
#define WARN_LOG(...) LogImpl(LogLevel::WARN, std::source_location::current(), __VA_ARGS__) #define WARN_LOG(...) LogImpl(LogLevel::WARN, std::source_location::current(), __VA_ARGS__)
#define ERROR_LOG(...) LogImpl(LogLevel::ERROR, std::source_location::current(), __VA_ARGS__) #define ERROR_LOG(...) LogImpl(LogLevel::ERROR, std::source_location::current(), __VA_ARGS__)
#ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif

View file

@ -1,15 +1,12 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstddef>
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <expected> #include <expected>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
#include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -183,8 +180,8 @@ using Option = std::optional<Tp>;
* @typedef Array * @typedef Array
* @brief Represents a fixed-size array. * @brief Represents a fixed-size array.
*/ */
template <typename Tp, std::size_t nm> template <typename Tp, usize sz>
using Array = std::array<Tp, nm>; using Array = std::array<Tp, sz>;
/** /**
* @typedef Vec * @typedef Vec
@ -265,17 +262,19 @@ enum class EnvError : u8 { NotFound, AccessError };
inline auto GetEnv(const String& name) -> Result<String, EnvError> { inline auto GetEnv(const String& name) -> Result<String, EnvError> {
#ifdef _WIN32 #ifdef _WIN32
char* rawPtr = nullptr; char* rawPtr = nullptr;
size_t bufferSize = 0; usize bufferSize = 0;
if (_dupenv_s(&rawPtr, &bufferSize, name.c_str()) != 0) const i32 err = _dupenv_s(&rawPtr, &bufferSize, name.c_str());
return std::unexpected(EnvError::AccessError);
if (!rawPtr) const UniquePointer<char, decltype(&free)> ptrManager(rawPtr, free);
return std::unexpected(EnvError::NotFound);
const String result(rawPtr); if (err != 0)
free(rawPtr); return Err(EnvError::AccessError);
return result;
if (!ptrManager)
return Err(EnvError::NotFound);
return ptrManager.get();
#else #else
CStr value = std::getenv(name.c_str()); CStr value = std::getenv(name.c_str());