hopefully i dont regret doing allat
This commit is contained in:
parent
39e5a6a509
commit
cd74075a2e
9 changed files with 365 additions and 313 deletions
21
meson.build
21
meson.build
|
@ -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 #
|
||||
|
|
|
@ -86,12 +86,13 @@ 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".
|
||||
|
||||
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.
|
||||
* @param tbl The TOML table to parse, containing [weather].
|
||||
|
@ -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 {},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
61
src/main.cpp
61
src/main.cpp
|
@ -96,7 +96,6 @@ 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
61
src/os/os.h
61
src/os/os.h
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
while (currentProcessInfo) {
|
||||
String processName = winrt::to_string(currentProcessInfo.ExecutableFileName());
|
||||
|
||||
if (!processName.empty()) {
|
||||
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")
|
||||
if (processName.length() > 4 && processName.ends_with(".exe"))
|
||||
processName.resize(processName.length() - 4);
|
||||
|
||||
auto iter = std::ranges::find_if(shellMap, [&](const auto& pair) {
|
||||
return std::string_view { processName } == pair.first;
|
||||
});
|
||||
auto iter =
|
||||
std::ranges::find_if(shellMap, [&](const auto& pair) { return StringView { processName } == pair.first; });
|
||||
|
||||
if (iter != std::ranges::end(shellMap))
|
||||
return String { iter->second };
|
||||
|
||||
pid = GetParentProcessId(pid);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
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 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 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]))))
|
||||
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 patch relies on registry ProductName only.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
|
||||
Vec<String> processNames;
|
||||
processNames.reserve(processInfo.size());
|
||||
for (const auto& name : processInfo | std::views::values) processNames.push_back(name);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
const std::unordered_map<String, String> wmProcesses = {
|
||||
{ "glazewm.exe", "GlazeWM" },
|
||||
{ "fancywm.exe", "FancyWM" },
|
||||
{ "komorebi.exe", "Komorebi" },
|
||||
{ "komorebic.exe", "Komorebi" }
|
||||
{ "komorebic.exe", "Komorebi" },
|
||||
};
|
||||
|
||||
for (const auto& [processName, wmName] : wmProcesses)
|
||||
if (IsProcessRunning(processNames, processName))
|
||||
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,21 +337,27 @@ 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")) {
|
||||
if (const Result<String, EnvError> msystemResult = GetEnv("MSYSTEM"); msystemResult && !msystemResult->empty()) {
|
||||
String shellPath;
|
||||
if (const Result<String, EnvError> shellResult = GetEnv("SHELL"); !shellResult->empty())
|
||||
|
||||
if (const Result<String, EnvError> shellResult = GetEnv("SHELL"); shellResult && !shellResult->empty())
|
||||
shellPath = *shellResult;
|
||||
else if (const Result<String, EnvError> loginShellResult = GetEnv("LOGINSHELL"); !loginShellResult->empty())
|
||||
else if (const Result<String, EnvError> loginShellResult = GetEnv("LOGINSHELL");
|
||||
loginShellResult && !loginShellResult->empty())
|
||||
shellPath = *loginShellResult;
|
||||
|
||||
if (!shellPath.empty()) {
|
||||
const usize lastSlash = shellPath.find_last_of("\\/");
|
||||
String shellExe = (lastSlash != String::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
|
||||
|
||||
std::ranges::transform(shellExe, shellExe.begin(), [](const u8 c) { return std::tolower(c); });
|
||||
std::ranges::transform(shellExe, shellExe.begin(), [](const u8 c) {
|
||||
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
});
|
||||
|
||||
if (shellExe.ends_with(".exe"))
|
||||
shellExe.resize(shellExe.length() - 4);
|
||||
|
@ -405,17 +377,20 @@ fn os::GetShell() -> String {
|
|||
|
||||
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 "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
|
||||
|
|
106
src/util/types.h
106
src/util/types.h
|
@ -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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[wrap-redirect]
|
||||
filename = SQLiteCpp-3.3.2/subprojects/sqlite3.wrap
|
Loading…
Add table
Add a link
Reference in a new issue