hopefully i dont regret doing allat

This commit is contained in:
Mars 2025-04-26 01:20:39 -04:00
parent 39e5a6a509
commit cd74075a2e
9 changed files with 365 additions and 313 deletions

View file

@ -48,9 +48,6 @@ common_cpp_flags = {
'/external:W0',
'/external:anglebrackets',
'/std:c++latest',
'/w44668',
'/w44710',
'/w44820',
],
'unix_extra' : [
'-march=native',
@ -128,6 +125,13 @@ elif host_system == 'windows'
cpp.find_library('windowsapp'),
]
elif host_system == 'linux'
dbus_cxx_dep = dependency('dbus-cxx', include_type : 'system', required : false)
if not dbus_cxx_dep.found()
cmake = import('cmake')
dbus_cxx_proj = cmake.subproject('dbus_cxx')
dbus_cxx_dep = dbus_cxx_proj.dependency('dbus_cxx', include_type : 'system')
endif
platform_deps += [
dependency('SQLiteCpp'),
dependency('xcb'),
@ -135,6 +139,7 @@ elif host_system == 'linux'
dependency('xdmcp'),
dependency('wayland-client'),
dependency('sigc++-3.0', include_type : 'system'),
dbus_cxx_dep,
]
endif
@ -158,14 +163,6 @@ if not ftxui_dep.found()
)
endif
dbus_cxx_dep = dependency('dbus-cxx', include_type : 'system', required : false)
if not dbus_cxx_dep.found()
cmake = import('cmake')
dbus_cxx_proj = cmake.subproject('dbus_cxx')
dbus_cxx_dep = dbus_cxx_proj.dependency('dbus_cxx', include_type : 'system')
endif
glaze_dep = dependency('glaze', include_type : 'system', required : false)
if not glaze_dep.found()
@ -175,7 +172,7 @@ if not glaze_dep.found()
endif
# Combine all dependencies
deps = common_deps + platform_deps + ftxui_dep + dbus_cxx_dep + glaze_dep
deps = common_deps + platform_deps + ftxui_dep + glaze_dep
# ------------------------- #
# Link/ObjC Configuration #

View file

@ -86,11 +86,12 @@ struct NowPlaying {
* @brief Holds configuration settings for the Weather feature.
*/
struct Weather {
bool enabled = false; ///< Flag to enable or disable the Weather feature.
bool show_town_name = false; ///< Flag to show the town name in the output.
Location location; ///< Location for weather data, can be a city name or coordinates.
String api_key; ///< API key for the weather service.
String units; ///< Units for temperature, either "metric" or "imperial".
Location location; ///< Location for weather data, can be a city name or coordinates.
String api_key; ///< API key for the weather service.
String units; ///< Units for temperature, either "metric" or "imperial".
bool enabled = false; ///< Flag to enable or disable the Weather feature.
bool show_town_name = false; ///< Flag to show the town name in the output.
/**
* @brief Parses a TOML table to create a Weather instance.
@ -143,8 +144,8 @@ struct Weather {
*/
struct Config {
General general; ///< General configuration settings.
NowPlaying now_playing; ///< Now Playing configuration settings.
Weather weather; ///< Weather configuration settings.`
NowPlaying now_playing; ///< Now Playing configuration settings.
/**
* @brief Parses a TOML table to create a Config instance.
@ -158,8 +159,8 @@ struct Config {
return {
.general = genTbl.is_table() ? General::fromToml(*genTbl.as_table()) : General {},
.now_playing = npTbl.is_table() ? NowPlaying::fromToml(*npTbl.as_table()) : NowPlaying {},
.weather = wthTbl.is_table() ? Weather::fromToml(*wthTbl.as_table()) : Weather {},
.now_playing = npTbl.is_table() ? NowPlaying::fromToml(*npTbl.as_table()) : NowPlaying {},
};
}

View file

@ -20,7 +20,7 @@ namespace {
}
}
fn SystemData::fetchSystemData(const Config& config) -> SystemData {
SystemData SystemData::fetchSystemData(const Config& config) {
SystemData data {
.date = GetDate(),
.host = os::GetHost(),
@ -29,36 +29,42 @@ fn SystemData::fetchSystemData(const Config& config) -> SystemData {
.mem_info = os::GetMemInfo(),
.desktop_environment = os::GetDesktopEnvironment(),
.window_manager = os::GetWindowManager(),
.now_playing = {},
.weather_info = {},
.disk_used = {},
.disk_total = {},
.shell = {},
.disk_usage = {},
.shell = None,
.now_playing = None,
.weather_info = None,
};
auto diskShell = std::async(std::launch::async, [] {
auto [used, total] = os::GetDiskUsage();
return std::make_tuple(used, total, os::GetShell());
auto diskShellFuture = std::async(std::launch::async, [] {
Result<DiskSpace, OsError> diskResult = os::GetDiskUsage();
Option<String> shellOption = os::GetShell();
return std::make_tuple(std::move(diskResult), std::move(shellOption));
});
std::future<WeatherOutput> weather;
std::future<Result<String, NowPlayingError>> nowPlaying;
std::future<WeatherOutput> weatherFuture;
std::future<Result<MediaInfo, NowPlayingError>> nowPlayingFuture;
if (config.weather.enabled)
weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); });
weatherFuture = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); });
if (config.now_playing.enabled)
nowPlaying = std::async(std::launch::async, os::GetNowPlaying);
nowPlayingFuture = std::async(std::launch::async, os::GetNowPlaying);
auto [used, total, shell] = diskShell.get();
data.disk_used = used;
data.disk_total = total;
data.shell = shell;
auto [diskResult, shellOption] = diskShellFuture.get();
if (weather.valid())
data.weather_info = weather.get();
if (nowPlaying.valid())
data.now_playing = nowPlaying.get();
data.disk_usage = std::move(diskResult);
data.shell = std::move(shellOption);
if (weatherFuture.valid())
try {
data.weather_info = weatherFuture.get();
} catch (const std::exception& e) {
ERROR_LOG("Failed to get weather info: {}", e.what());
data.weather_info = None;
}
if (nowPlayingFuture.valid())
data.now_playing = nowPlayingFuture.get();
return data;
}

View file

@ -56,21 +56,20 @@ struct std::formatter<BytesToGiB> : std::formatter<double> {
* in order to display it at all at once during runtime.
*/
struct SystemData {
using NowPlayingResult = Option<Result<String, NowPlayingError>>;
using NowPlayingResult = Option<Result<MediaInfo, NowPlayingError>>;
// clang-format off
String date; ///< Current date (e.g., "April 24th").
String host; ///< Host or product family name (e.g., "MacBook Pro").
String kernel_version; ///< OS kernel version (e.g., "5.15.0-generic").
Result<String, String> os_version; ///< OS pretty name (e.g., "Ubuntu 22.04 LTS") or an error message.
Result<u64, String> mem_info; ///< Total physical RAM in bytes or an error message.
Option<String> desktop_environment; ///< Detected desktop environment (e.g., "GNOME", "KDE", "Fluent (Windows 11)"). Might be None.
Option<String> window_manager; ///< Detected window manager (e.g., "Mutter", "KWin", "DWM").
NowPlayingResult now_playing; ///< Currently playing media ("Artist - Title") or an error/None if disabled/unavailable.
Option<WeatherOutput> weather_info; ///< Weather information or None if disabled/unavailable.
u64 disk_used; ///< Used disk space in bytes for the root filesystem.
u64 disk_total; ///< Total disk space in bytes for the root filesystem.
String shell; ///< Name of the current user shell (e.g., "Bash", "Zsh", "PowerShell").
String date; ///< Current date (e.g., "April 26th"). Always expected to succeed.
Result<String, OsError> host; ///< Host/product family (e.g., "MacBookPro18,3") or OS error.
Result<String, OsError> kernel_version; ///< OS kernel version (e.g., "23.4.0") or OS error.
Result<String, OsError> os_version; ///< OS pretty name (e.g., "macOS Sonoma 14.4.1") or OS error.
Result<u64, OsError> mem_info; ///< Total physical RAM in bytes or OS error.
Option<String> desktop_environment; ///< Detected desktop environment (e.g., "Aqua", "Plasma"). None if not detected/applicable.
Option<String> window_manager; ///< Detected window manager (e.g., "Quartz Compositor", "KWin"). None if not detected/applicable.
Result<DiskSpace, OsError> disk_usage; ///< Used/Total disk space for root filesystem or OS error.
Option<String> shell; ///< Name of the current user shell (e.g., "zsh"). None if not detected.
NowPlayingResult now_playing; ///< Optional: Result of fetching media info (MediaInfo on success, NowPlayingError on failure). None if disabled.
Option<WeatherOutput> weather_info; ///< Optional: Weather information. None if disabled or error during fetch.
// clang-format on
/**

View file

@ -94,9 +94,8 @@ namespace {
}
fn SystemInfoBox(const Config& config, const SystemData& data) -> Element {
const String& name = config.general.name;
const Weather weather = config.weather;
const bool nowPlayingEnabled = config.now_playing.enabled;
const String& name = config.general.name;
const Weather weather = config.weather;
const auto& [userIcon, paletteIcon, calendarIcon, hostIcon, kernelIcon, osIcon, memoryIcon, weatherIcon, musicIcon, diskIcon, shellIcon, deIcon, wmIcon] =
ui::SHOW_ICONS ? ui::NERD_ICONS : ui::EMPTY_ICONS;
@ -114,11 +113,11 @@ namespace {
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
// Helper function for aligned rows
fn createRow = [&](const auto& icon, const auto& label, const auto& value) {
fn createRow = [&](const StringView& icon, const StringView& label, const StringView& value) { // NEW
return hbox(
{
text(String(icon)) | color(ui::DEFAULT_THEME.icon),
text(String(static_cast<CStr>(label))) | color(ui::DEFAULT_THEME.label),
text(String(label)) | color(ui::DEFAULT_THEME.label),
filler(),
text(String(value)) | color(ui::DEFAULT_THEME.value),
text(" "),
@ -171,28 +170,38 @@ namespace {
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
if (!data.host.empty())
content.push_back(createRow(hostIcon, "Host", data.host));
if (data.host)
content.push_back(createRow(hostIcon, "Host", *data.host));
else
ERROR_LOG("Failed to get host info: {}", data.host.error().message);
if (!data.kernel_version.empty())
content.push_back(createRow(kernelIcon, "Kernel", data.kernel_version));
if (data.kernel_version)
content.push_back(createRow(kernelIcon, "Kernel", *data.kernel_version));
else
ERROR_LOG("Failed to get kernel version: {}", data.kernel_version.error().message);
if (data.os_version)
content.push_back(createRow(String(osIcon), "OS", *data.os_version));
else
ERROR_LOG("Failed to get OS version: {}", data.os_version.error());
ERROR_LOG("Failed to get OS version: {}", data.os_version.error().message);
if (data.mem_info)
content.push_back(createRow(memoryIcon, "RAM", std::format("{}", BytesToGiB { *data.mem_info })));
else
ERROR_LOG("Failed to get memory info: {}", data.mem_info.error());
ERROR_LOG("Failed to get memory info: {}", data.mem_info.error().message);
// Add Disk usage row
content.push_back(
createRow(diskIcon, "Disk", std::format("{}/{}", BytesToGiB { data.disk_used }, BytesToGiB { data.disk_total }))
);
if (data.disk_usage)
content.push_back(createRow(
diskIcon,
"Disk",
std::format("{}/{}", BytesToGiB { data.disk_usage->used_bytes }, BytesToGiB { data.disk_usage->total_bytes })
));
else
ERROR_LOG("Failed to get disk usage: {}", data.disk_usage.error().message);
content.push_back(createRow(shellIcon, "Shell", data.shell));
if (data.shell)
content.push_back(createRow(shellIcon, "Shell", *data.shell));
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
@ -202,10 +211,13 @@ namespace {
if (data.window_manager)
content.push_back(createRow(wmIcon, "WM", *data.window_manager));
// Now Playing row
if (nowPlayingEnabled && data.now_playing) {
if (const Result<String, NowPlayingError>& nowPlayingResult = *data.now_playing; nowPlayingResult.has_value()) {
const String& npText = *nowPlayingResult;
if (config.now_playing.enabled && data.now_playing) {
if (const Result<MediaInfo, NowPlayingError>& nowPlayingResult = *data.now_playing) {
const MediaInfo& info = *nowPlayingResult;
const String title = info.title.value_or("Unknown Title");
const String artist = info.artist.value_or("Unknown Artist");
const String npText = artist + " - " + title;
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
content.push_back(hbox(
@ -219,22 +231,13 @@ namespace {
}
));
} else {
const NowPlayingError& error = nowPlayingResult.error();
if (std::holds_alternative<NowPlayingCode>(error))
if (const NowPlayingError& error = nowPlayingResult.error(); std::holds_alternative<NowPlayingCode>(error)) {
switch (std::get<NowPlayingCode>(error)) {
case NowPlayingCode::NoPlayers: DEBUG_LOG("No players found"); break;
case NowPlayingCode::NoActivePlayer: DEBUG_LOG("No active player found"); break;
default: std::unreachable();
}
#ifdef _WIN32
if (std::holds_alternative<WindowsError>(error))
DEBUG_LOG("WinRT error: {}", to_string(std::get<WindowsError>(error).message()));
#else
if (std::holds_alternative<String>(error))
DEBUG_LOG("NowPlaying error: {}", std::get<String>(error));
#endif
} else
ERROR_LOG("Failed to get now playing info: {}", std::get<OsError>(error).message);
}
}

View file

@ -16,27 +16,27 @@ namespace os {
/**
* @brief Get the total amount of physical RAM installed in the system.
* @return A Result containing the total RAM in bytes (u64) on success,
* or an error message (String) on failure.
* or an OsError on failure.
*/
fn GetMemInfo() -> Result<u64, String>;
fn GetMemInfo() -> Result<u64, OsError>;
/**
* @brief Gets metadata about the currently playing media.
* @return A Result containing the media information (String) on success,
* or an error code (NowPlayingError) on failure.
* @brief Gets structured metadata about the currently playing media.
* @return A Result containing the media information (MediaInfo struct) on success,
* or a NowPlayingError (indicating player state or system error) on failure.
*/
fn GetNowPlaying() -> Result<String, NowPlayingError>;
fn GetNowPlaying() -> Result<MediaInfo, NowPlayingError>;
/**
* @brief Gets the "pretty" name of the operating system.
* @details Examples: "Ubuntu 24.04.2 LTS", "Windows 11 Pro 24H2", "macOS 15 Sequoia".
* @return A Result containing the OS version String on success,
* or an error message (String) on failure.
* or an OsError on failure.
*/
fn GetOSVersion() -> Result<String, String>;
fn GetOSVersion() -> Result<String, OsError>;
/**
* @brief Attempts to retrieve the desktop environment.
* @brief Attempts to retrieve the desktop environment name.
* @details This is most relevant on Linux. May check environment variables (XDG_CURRENT_DESKTOP),
* session files, or running processes. On Windows/macOS, it might return a
* UI theme identifier (e.g., "Fluent", "Aqua") or None.
@ -45,53 +45,54 @@ namespace os {
fn GetDesktopEnvironment() -> Option<String>;
/**
* @brief Attempts to retrieve the window manager.
* @brief Attempts to retrieve the window manager name.
* @details On Linux, checks Wayland compositor or X11 WM properties. On Windows, returns "DWM" or similar.
* On macOS, might return "Quartz Compositor" or a specific tiling WM name if active.
* @return A String containing the detected WM name, or None if detection fails or is not applicable.
* @return An Option containing the detected WM name String, or None if detection fails.
*/
fn GetWindowManager() -> Option<String>;
/**
* @brief Attempts to detect the current user shell.
* @brief Attempts to detect the current user shell name.
* @details Checks the SHELL environment variable on Linux/macOS. On Windows, inspects the process tree
* to identify known shells like PowerShell, Cmd, or MSYS2 shells (Bash, Zsh).
* @return A String containing the detected shell name (e.g., "Bash", "Zsh", "PowerShell", "Fish").
* May return the full path or "Unknown" as a fallback.
* @return An Option containing the detected shell name (e.g., "Bash", "Zsh", "PowerShell", "Fish"), or None if
* detection fails.
*/
fn GetShell() -> String;
fn GetShell() -> Option<String>;
/**
* @brief Gets a system identifier, often the hardware model or product family.
* @details Examples: "MacBookPro18,3", "Latitude 5420", "ThinkPad T490".
* Implementation varies: reads DMI info on Linux, registry on Windows, sysctl on macOS.
* @return A String containing the host/product identifier. May be empty if retrieval fails.
* @return A Result containing the host/product identifier String on success,
* or an OsError on failure (e.g., permission reading DMI/registry, API error).
*/
fn GetHost() -> String;
fn GetHost() -> Result<String, OsError>;
/**
* @brief Gets the operating system's kernel version string.
* @details Examples: "5.15.0-76-generic", "10.0.22621", "23.1.0".
* Uses uname() on Linux/macOS, WinRT/registry on Windows.
* @return A String containing the kernel version. May be empty if retrieval fails.
* @return A Result containing the kernel version String on success,
* or an OsError on failure.
*/
fn GetKernelVersion() -> String;
fn GetKernelVersion() -> Result<String, OsError>;
/**
* @brief Gets the number of installed packages (Linux-specific).
* @details Sums counts from various package managers (dpkg, rpm, pacman, flatpak, snap, etc.).
* Returns 0 on non-Linux platforms or if no package managers are found.
* @return A u64 representing the total count of detected packages.
* @brief Gets the number of installed packages (Platform-specific).
* @details On Linux, sums counts from various package managers. On other platforms, behavior may vary.
* @return A Result containing the package count (u64) on success,
* or an OsError on failure (e.g., permission errors, command not found)
* or if not supported (OsErrorCode::NotSupported).
*/
fn GetPackageCount() -> u64; // Note: Implementation likely exists only in linux.cpp
fn GetPackageCount() -> Result<u64, OsError>; // Note: Returns OsError{OsErrorCode::NotSupported} on Win/Mac likely
/**
* @brief Gets the disk usage for the primary/root filesystem.
* @details Uses statvfs on Linux/macOS, GetDiskFreeSpaceExW on Windows.
* @return A Pair<u64, u64> where:
* - first: Used disk space in bytes.
* - second: Total disk space in bytes.
* Returns {0, 0} on failure.
* @return A Result containing the DiskSpace struct (used/total bytes) on success,
* or an OsError on failure (e.g., filesystem not found, permission error).
*/
fn GetDiskUsage() -> Pair<u64, u64>;
}
fn GetDiskUsage() -> Result<DiskSpace, OsError>;
} // namespace os

View file

@ -5,12 +5,11 @@
#include <windows.h>
#include <wincrypt.h>
#include <dwmapi.h>
#include <tlhelp32.h>
// clang-format on
#include <cstring>
#include <ranges>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Media.Control.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.System.Diagnostics.h>
@ -60,47 +59,6 @@ namespace {
}};
// clang-format on
class ProcessSnapshot {
public:
ProcessSnapshot() : h_snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) {}
ProcessSnapshot(ProcessSnapshot&&) = delete;
ProcessSnapshot(const ProcessSnapshot&) = delete;
fn operator=(ProcessSnapshot&&)->ProcessSnapshot& = delete;
fn operator=(const ProcessSnapshot&)->ProcessSnapshot& = delete;
~ProcessSnapshot() {
if (h_snapshot != INVALID_HANDLE_VALUE)
CloseHandle(h_snapshot);
}
[[nodiscard]] fn isValid() const -> bool { return h_snapshot != INVALID_HANDLE_VALUE; }
[[nodiscard]] fn getProcesses() const -> Vec<Pair<DWORD, String>> {
Vec<Pair<DWORD, String>> processes;
if (!isValid())
return processes;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(h_snapshot, &pe32))
return processes;
if (Process32First(h_snapshot, &pe32)) {
processes.emplace_back(pe32.th32ProcessID, String(reinterpret_cast<const char*>(pe32.szExeFile)));
while (Process32Next(h_snapshot, &pe32))
processes.emplace_back(pe32.th32ProcessID, String(reinterpret_cast<const char*>(pe32.szExeFile)));
}
return processes;
}
HANDLE h_snapshot;
};
fn GetRegistryValue(const HKEY& hKey, const String& subKey, const String& valueName) -> String {
HKEY key = nullptr;
if (RegOpenKeyExA(hKey, subKey.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS)
@ -125,88 +83,74 @@ namespace {
return value;
}
fn GetProcessInfo() -> Vec<Pair<DWORD, String>> {
const ProcessSnapshot snapshot;
return snapshot.isValid() ? snapshot.getProcesses() : std::vector<std::pair<DWORD, String>> {};
fn GetProcessInfo() -> Result<Vec<Pair<DWORD, String>>, OsError> {
try {
using namespace winrt::Windows::System::Diagnostics;
using namespace winrt::Windows::Foundation::Collections;
const IVectorView<ProcessDiagnosticInfo> processInfos = ProcessDiagnosticInfo::GetForProcesses();
Vec<Pair<DWORD, String>> processes;
processes.reserve(processInfos.Size());
for (const auto& processInfo : processInfos)
processes.emplace_back(processInfo.ProcessId(), winrt::to_string(processInfo.ExecutableFileName()));
return processes;
} catch (const winrt::hresult_error& e) { return Err(OsError(e)); } catch (const std::exception& e) {
return Err(OsError(e));
}
}
fn IsProcessRunning(const Vec<String>& processes, const String& name) -> bool {
return std::ranges::any_of(processes, [&name](const String& proc) -> bool {
fn IsProcessRunning(const Vec<String>& processNames, const String& name) -> bool {
return std::ranges::any_of(processNames, [&name](const String& proc) -> bool {
return _stricmp(proc.c_str(), name.c_str()) == 0;
});
}
fn GetParentProcessId(const DWORD pid) -> DWORD {
const ProcessSnapshot snapshot;
if (!snapshot.isValid())
return 0;
PROCESSENTRY32 pe32 { .dwSize = sizeof(PROCESSENTRY32) };
if (!Process32First(snapshot.h_snapshot, &pe32))
return 0;
if (pe32.th32ProcessID == pid)
return pe32.th32ParentProcessID;
while (Process32Next(snapshot.h_snapshot, &pe32))
if (pe32.th32ProcessID == pid)
return pe32.th32ParentProcessID;
return 0;
}
fn GetProcessName(const DWORD pid) -> String {
const ProcessSnapshot snapshot;
if (!snapshot.isValid())
return "";
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(snapshot.h_snapshot, &pe32))
return "";
if (pe32.th32ProcessID == pid)
return reinterpret_cast<const char*>(pe32.szExeFile);
while (Process32Next(snapshot.h_snapshot, &pe32))
if (pe32.th32ProcessID == pid)
return reinterpret_cast<const char*>(pe32.szExeFile);
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);
-> Option<String> {
if (startPid == 0)
return None;
if (processName.empty()) {
pid = GetParentProcessId(pid);
continue;
try {
using namespace winrt::Windows::System::Diagnostics;
ProcessDiagnosticInfo currentProcessInfo = nullptr;
try {
currentProcessInfo = ProcessDiagnosticInfo::TryGetForProcessId(startPid);
} catch (const winrt::hresult_error& e) {
RETURN_ERR("Failed to get process info for PID {}: {}", startPid, winrt::to_string(e.message()));
}
std::ranges::transform(processName, processName.begin(), [](const u8 character) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(character)));
});
while (currentProcessInfo) {
String processName = winrt::to_string(currentProcessInfo.ExecutableFileName());
if (processName.length() > 4 && processName.substr(processName.length() - 4) == ".exe")
processName.resize(processName.length() - 4);
if (!processName.empty()) {
std::ranges::transform(processName, processName.begin(), [](const u8 character) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(character)));
});
auto iter = std::ranges::find_if(shellMap, [&](const auto& pair) {
return std::string_view { processName } == pair.first;
});
if (processName.length() > 4 && processName.ends_with(".exe"))
processName.resize(processName.length() - 4);
if (iter != std::ranges::end(shellMap))
return String { iter->second };
auto iter =
std::ranges::find_if(shellMap, [&](const auto& pair) { return StringView { processName } == pair.first; });
pid = GetParentProcessId(pid);
if (iter != std::ranges::end(shellMap))
return String { iter->second };
}
currentProcessInfo = currentProcessInfo.Parent();
}
} catch (const winrt::hresult_error& e) {
ERROR_LOG("WinRT error during process tree walk (start PID {}): {}", startPid, winrt::to_string(e.message()));
} catch (const std::exception& e) {
ERROR_LOG("Standard exception during process tree walk (start PID {}): {}", startPid, e.what());
}
return std::nullopt;
return None;
}
fn GetBuildNumber() -> Option<u64> {
@ -227,36 +171,40 @@ namespace {
}
}
fn os::GetMemInfo() -> Result<u64, String> {
fn os::GetMemInfo() -> Result<u64, OsError> {
try {
return winrt::Windows::System::Diagnostics::SystemDiagnosticInfo::GetForCurrentSystem()
.MemoryUsage()
.GetReport()
.TotalPhysicalSizeInBytes();
} catch (const winrt::hresult_error& e) {
return Err(std::format("Failed to get memory info: {}", to_string(e.message())));
}
} catch (const winrt::hresult_error& e) { return Err(OsError(e)); }
}
fn os::GetNowPlaying() -> Result<String, NowPlayingError> {
fn os::GetNowPlaying() -> Result<MediaInfo, NowPlayingError> {
using namespace winrt::Windows::Media::Control;
using namespace winrt::Windows::Foundation;
using Session = GlobalSystemMediaTransportControlsSession;
using SessionManager = GlobalSystemMediaTransportControlsSessionManager;
using Session = GlobalSystemMediaTransportControlsSession;
using SessionManager = GlobalSystemMediaTransportControlsSessionManager;
using MediaProperties = GlobalSystemMediaTransportControlsSessionMediaProperties;
try {
const IAsyncOperation<SessionManager> sessionManagerOp = SessionManager::RequestAsync();
const SessionManager sessionManager = sessionManagerOp.get();
if (const Session currentSession = sessionManager.GetCurrentSession())
return winrt::to_string(currentSession.TryGetMediaPropertiesAsync().get().Title());
if (const Session currentSession = sessionManager.GetCurrentSession()) {
const MediaProperties mediaProperties = currentSession.TryGetMediaPropertiesAsync().get();
return MediaInfo(
winrt::to_string(mediaProperties.Title()), winrt::to_string(mediaProperties.Artist()), None, None
);
}
return Err(NowPlayingCode::NoActivePlayer);
} catch (const winrt::hresult_error& e) { return Err(e); }
} catch (const winrt::hresult_error& e) { return Err(OsError(e)); }
}
fn os::GetOSVersion() -> Result<String, String> {
fn os::GetOSVersion() -> Result<String, OsError> {
try {
const String regSubKey = R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)";
@ -264,26 +212,33 @@ fn os::GetOSVersion() -> Result<String, String> {
const String displayVersion = GetRegistryValue(HKEY_LOCAL_MACHINE, regSubKey, "DisplayVersion");
if (productName.empty())
return Err("Failed to read ProductName");
return Err(OsError { OsErrorCode::NotFound, "ProductName not found in registry" });
if (const Option<u64> buildNumber = GetBuildNumber()) {
if (*buildNumber >= 22000)
if (const usize pos = productName.find("Windows 10");
pos != String::npos && (pos == 0 || !isalnum(static_cast<u8>(productName[pos - 1]))) &&
(pos + 10 == productName.length() || !isalnum(static_cast<u8>(productName[pos + 10]))))
productName.replace(pos, 10, "Windows 11");
} else
DEBUG_LOG("Warning: Could not get build number via WinRT; Win11 patch relies on registry ProductName only.");
if (const Option<u64> buildNumberOpt = GetBuildNumber()) {
if (const u64 buildNumber = *buildNumberOpt; buildNumber >= 22000) {
if (const size_t pos = productName.find("Windows 10"); pos != String::npos) {
const bool startBoundary = (pos == 0 || !isalnum(static_cast<unsigned char>(productName[pos - 1])));
const bool endBoundary =
(pos + 10 == productName.length() || !isalnum(static_cast<unsigned char>(productName[pos + 10])));
if (startBoundary && endBoundary) {
productName.replace(pos, 10, "Windows 11");
}
}
}
} else {
DEBUG_LOG("Warning: Could not get build number via WinRT; Win11 detection might be inaccurate.");
}
return displayVersion.empty() ? productName : productName + " " + displayVersion;
} catch (const Exception& e) { return Err(std::format("Exception occurred getting OS version: {}", e.what())); }
} catch (const std::exception& e) { return Err(OsError(e)); }
}
fn os::GetHost() -> String {
fn os::GetHost() -> Result<String, OsError> {
return GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily");
}
fn os::GetKernelVersion() -> String {
fn os::GetKernelVersion() -> Result<String, OsError> {
try {
using namespace winrt::Windows::System::Profile;
@ -292,36 +247,47 @@ fn os::GetKernelVersion() -> String {
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);
} catch (const winrt::hresult_error& e) {
ERROR_LOG("WinRT error: {}", winrt::to_string(e.message()));
} catch (const Exception& e) { ERROR_LOG("Failed to get kernel version: {}", e.what()); }
} catch (const winrt::hresult_error& e) { return Err(OsError(e)); } catch (const Exception& e) {
return Err(OsError(e));
}
return "";
return Err(OsError { OsErrorCode::NotFound, "Could not determine kernel version" });
}
fn os::GetWindowManager() -> String {
const auto processInfo = GetProcessInfo();
std::vector<String> processNames;
fn os::GetWindowManager() -> Option<String> {
if (const Result<Vec<Pair<DWORD, String>>, OsError> processInfoResult = GetProcessInfo()) {
const Vec<Pair<DWORD, String>>& processInfo = *processInfoResult;
processNames.reserve(processInfo.size());
for (const auto& name : processInfo | std::views::values) processNames.push_back(name);
Vec<String> processNames;
processNames.reserve(processInfo.size());
const std::unordered_map<String, String> wmProcesses = {
{ "glazewm.exe", "GlazeWM" },
{ "fancywm.exe", "FancyWM" },
{ "komorebi.exe", "Komorebi" },
{ "komorebic.exe", "Komorebi" }
};
for (const String& val : processInfo | std::views::values) {
if (!val.empty()) {
const usize lastSlash = val.find_last_of("/\\");
processNames.push_back(lastSlash == String::npos ? val : val.substr(lastSlash + 1));
}
}
for (const auto& [processName, wmName] : wmProcesses)
if (IsProcessRunning(processNames, processName))
return wmName;
const std::unordered_map<String, String> wmProcesses = {
{ "glazewm.exe", "GlazeWM" },
{ "fancywm.exe", "FancyWM" },
{ "komorebi.exe", "Komorebi" },
{ "komorebic.exe", "Komorebi" },
};
for (const auto& [processExe, wmName] : wmProcesses)
if (IsProcessRunning(processNames, processExe))
return wmName;
} else {
ERROR_LOG("Failed to get process info for WM detection: {}", processInfoResult.error().message);
}
BOOL compositionEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)))
return compositionEnabled ? "DWM" : "Windows Manager (Basic)";
return "Windows Manager";
return None;
}
fn os::GetDesktopEnvironment() -> Option<String> {
@ -371,51 +337,60 @@ fn os::GetDesktopEnvironment() -> Option<String> {
}
}
fn os::GetShell() -> String {
const DWORD currentPid = GetCurrentProcessId();
fn os::GetShell() -> Option<String> {
try {
const DWORD currentPid =
winrt::Windows::System::Diagnostics::ProcessDiagnosticInfo::GetForCurrentProcess().ProcessId();
if (const Result<String, EnvError> msystemResult = GetEnv("MSYSTEM")) {
String shellPath;
if (const Result<String, EnvError> shellResult = GetEnv("SHELL"); !shellResult->empty())
shellPath = *shellResult;
else if (const Result<String, EnvError> loginShellResult = GetEnv("LOGINSHELL"); !loginShellResult->empty())
shellPath = *loginShellResult;
if (const Result<String, EnvError> msystemResult = GetEnv("MSYSTEM"); msystemResult && !msystemResult->empty()) {
String shellPath;
if (!shellPath.empty()) {
const usize lastSlash = shellPath.find_last_of("\\/");
String shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
if (const Result<String, EnvError> shellResult = GetEnv("SHELL"); shellResult && !shellResult->empty())
shellPath = *shellResult;
else if (const Result<String, EnvError> loginShellResult = GetEnv("LOGINSHELL");
loginShellResult && !loginShellResult->empty())
shellPath = *loginShellResult;
std::ranges::transform(shellExe, shellExe.begin(), [](const u8 c) { return std::tolower(c); });
if (!shellPath.empty()) {
const usize lastSlash = shellPath.find_last_of("\\/");
String shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
if (shellExe.ends_with(".exe"))
shellExe.resize(shellExe.length() - 4);
std::ranges::transform(shellExe, shellExe.begin(), [](const u8 c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
const auto iter =
std::ranges::find_if(msysShellMap, [&](const auto& pair) { return StringView { shellExe } == pair.first; });
if (shellExe.ends_with(".exe"))
shellExe.resize(shellExe.length() - 4);
if (iter != std::ranges::end(msysShellMap))
return String { iter->second };
const auto iter =
std::ranges::find_if(msysShellMap, [&](const auto& pair) { return StringView { shellExe } == pair.first; });
if (iter != std::ranges::end(msysShellMap))
return String { iter->second };
}
if (const Option<String> msysShell = FindShellInProcessTree(currentPid, msysShellMap))
return *msysShell;
return "MSYS2 Environment";
}
if (const Option<String> msysShell = FindShellInProcessTree(currentPid, msysShellMap))
return *msysShell;
if (const Option<String> windowsShell = FindShellInProcessTree(currentPid, windowsShellMap))
return *windowsShell;
} catch (const winrt::hresult_error& e) {
ERROR_LOG("WinRT error during shell detection: {}", winrt::to_string(e.message()));
} catch (const std::exception& e) { ERROR_LOG("Standard exception during shell detection: {}", e.what()); }
return "MSYS2 Environment";
}
if (const Option<String> windowsShell = FindShellInProcessTree(currentPid, windowsShellMap))
return *windowsShell;
return "Unknown Shell";
return None;
}
fn os::GetDiskUsage() -> Pair<u64, u64> {
fn os::GetDiskUsage() -> Result<DiskSpace, OsError> {
ULARGE_INTEGER freeBytes, totalBytes;
if (GetDiskFreeSpaceExW(L"C:\\", nullptr, &totalBytes, &freeBytes))
return { totalBytes.QuadPart - freeBytes.QuadPart, totalBytes.QuadPart };
return DiskSpace { .used_bytes = totalBytes.QuadPart - freeBytes.QuadPart, .total_bytes = totalBytes.QuadPart };
return { 0, 0 };
return Err(OsError { OsErrorCode::NotFound, "Failed to get disk usage" });
}
#endif

View file

@ -1,7 +1,6 @@
#pragma once
#include <array> // std::array alias (Array)
#include <cmath> // std::float_t, std::double_t (f32/f64)
#include <cstdlib> // std::getenv, std::free
#include <expected> // std::expected alias (Result)
#include <map> // std::map alias (Map)
@ -36,8 +35,8 @@ using i64 = std::int64_t; ///< 64-bit signed integer.
// Provides concise names for standard floating-point types. //
//-----------------------------------------------------------//
using f32 = std::float_t; ///< 32-bit floating-point number.
using f64 = std::double_t; ///< 64-bit floating-point number.
using f32 = float; ///< 32-bit floating-point number.
using f64 = double; ///< 64-bit floating-point number.
//-------------------------------------------------//
// Size Type Aliases //
@ -155,25 +154,98 @@ enum class NowPlayingCode : u8 {
NoActivePlayer, ///< Players were found, but none are currently active or playing.
};
#ifdef _WIN32
using WindowsError = winrt::hresult_error; ///< Alias for WinRT HRESULT error type.
#endif
/**
* @enum OsErrorCode
* @brief Error codes for general OS-level operations.
*/
enum class OsErrorCode : u8 {
ApiUnavailable, ///< An underlying OS API failed, is unavailable, or returned an error.
BufferTooSmall, ///< A pre-allocated buffer was insufficient (less common with dynamic allocation).
InternalError, ///< An unspecified internal error within the abstraction layer.
IoError, ///< A general input/output error occurred.
NetworkError, ///< Network-related error (relevant if OS functions involve network).
NotFound, ///< A required resource (file, registry key, device) was not found.
NotSupported, ///< The requested operation is not supported on this platform or configuration.
ParseError, ///< Failed to parse data obtained from the OS (e.g., file content, API output).
PermissionDenied, ///< Insufficient permissions to perform the operation.
PlatformSpecific, ///< An error specific to the platform occurred (check message for details).
Success, ///< Operation completed successfully (often implicit).
Timeout, ///< An operation timed out (e.g., waiting for DBus reply).
Other, ///< A generic error code for unclassified errors.
};
/**
* @typedef NowPlayingError
* @brief Represents the possible errors returned by "Now Playing" functions.
* It's a variant that can hold either a generic NowPlayingCode,
* a platform-specific error (WindowsError on Windows, String on others),
* or potentially other error types if extended.
* @struct OsError
* @brief Holds structured information about an OS-level error.
*
* Used as the error type in Result for many os:: functions.
*/
using NowPlayingError = std::variant<
NowPlayingCode,
struct OsError {
String message = "Unknown Error"; ///< A descriptive error message, potentially including platform details.
OsErrorCode code = OsErrorCode::Other; ///< The general category of the error.
OsError(const OsErrorCode errc, String msg) : message(std::move(msg)), code(errc) {}
explicit OsError(const Exception& e) : message(e.what()) {}
#ifdef _WIN32
WindowsError
#else
String
explicit OsError(const winrt::hresult_error& e)
: message(winrt::to_string(e.message())), code(OsErrorCode::PlatformSpecific) {}
#endif
>;
#ifndef _WIN32
OsError(OsErrorCode c, int errno_val) : code(c), message(std::system_category().message(errno_val)) {}
#endif
};
/**
* @struct DiskSpace
* @brief Represents disk usage information.
*
* Used as the success type for os::GetDiskUsage.
*/
struct DiskSpace {
u64 used_bytes; ///< Currently used disk space in bytes.
u64 total_bytes; ///< Total disk space in bytes.
};
/**
* @struct MediaInfo
* @brief Holds structured metadata about currently playing media.
*
* Used as the success type for os::GetNowPlaying.
* Using Option<> for fields that might not always be available.
*/
struct MediaInfo {
/**
* @enum PlaybackStatus
* @brief Represents the playback status of the media player.
*/
enum class PlaybackStatus : u8 { Playing, Paused, Stopped, Unknown };
Option<String> title; ///< Track title.
Option<String> artist; ///< Track artist(s).
Option<String> album; ///< Album name.
Option<String> app_name; ///< Name of the media player application (e.g., "Spotify", "Firefox").
PlaybackStatus status = PlaybackStatus::Unknown; ///< Current playback status.
MediaInfo(Option<String> t, Option<String> a, Option<String> al, Option<String> app)
: title(std::move(t)), artist(std::move(a)), album(std::move(al)), app_name(std::move(app)) {}
};
//--------------------------------------------------------//
// Potentially Update Existing Application-Specific Types //
//--------------------------------------------------------//
/**
* @typedef NowPlayingError (Updated Recommendation)
* @brief Represents the possible errors returned by os::GetNowPlaying.
*
* It's a variant that can hold either a specific NowPlayingCode
* (indicating player state like 'no active player') or a general OsError
* (indicating an underlying system/API failure).
*/
using NowPlayingError = std::variant<NowPlayingCode, OsError>;
/**
* @enum EnvError

View file

@ -1,2 +0,0 @@
[wrap-redirect]
filename = SQLiteCpp-3.3.2/subprojects/sqlite3.wrap