diff --git a/.clang-tidy b/.clang-tidy index 10a5a5f..98bf1c6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,34 +1,35 @@ # noinspection SpellCheckingInspection Checks: > - *, - -ctad-maybe-unsupported, - -abseil-*, - -altera-*, - -boost-*, - -bugprone-easily-swappable-parameters, - -bugprone-implicit-widening-of-multiplication-result, - -cert-env33-c, - -concurrency-mt-unsafe, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-macro-usage, - -cppcoreguidelines-owning-memory, - -cppcoreguidelines-pro-type-member-init, - -cppcoreguidelines-pro-type-vararg, - -fuchsia-*, - -google-*, - -hicpp-*, - -llvm-include-order, - -llvm-include-order, - -llvm-namespace-comment, - -llvmlibc-*, - -misc-non-private-member-variables-in-classes, - -modernize-use-nullptr, - -readability-avoid-nested-conditional-operator, - -readability-braces-around-statements, - -readability-function-cognitive-complexity, - -readability-implicit-bool-conversion, - -readability-isolate-declaration, - -readability-magic-numbers + *, + -ctad-maybe-unsupported, + -abseil-*, + -altera-*, + -boost-*, + -bugprone-easily-swappable-parameters, + -bugprone-implicit-widening-of-multiplication-result, + -cert-env33-c, + -concurrency-mt-unsafe, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-member-init, + -cppcoreguidelines-pro-type-vararg, + -fuchsia-*, + -google-*, + -hicpp-*, + -llvm-include-order, + -llvm-include-order, + -llvm-namespace-comment, + -llvmlibc-*, + -misc-non-private-member-variables-in-classes, + -modernize-use-nullptr, + -readability-avoid-nested-conditional-operator, + -readability-braces-around-statements, + -readability-function-cognitive-complexity, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers CheckOptions: readability-identifier-naming.ClassCase: CamelCase readability-identifier-naming.EnumCase: CamelCase diff --git a/meson.build b/meson.build index 51ab1f4..15187bd 100644 --- a/meson.build +++ b/meson.build @@ -89,7 +89,7 @@ add_project_arguments(common_cpp_args, language : 'cpp') # ------- # # Files # # ------- # -base_sources = files('src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp') +base_sources = files('src/core/system_data.cpp', 'src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp') platform_sources = { 'linux' : ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp', 'src/os/linux/display_guards.cpp'], diff --git a/src/config/config.cpp b/src/config/config.cpp index f4d9ad5..cc203b4 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -74,13 +74,13 @@ namespace { String defaultName = GetUserNameA(username.data(), &size) ? username.data() : "User"; #else - const struct passwd* pwd = getpwuid(getuid()); - CStr pwdName = pwd ? pwd->pw_name : nullptr; + const passwd* pwd = getpwuid(getuid()); + CStr pwdName = pwd ? pwd->pw_name : nullptr; const Result envUser = GetEnv("USER"); const Result envLogname = GetEnv("LOGNAME"); - String defaultName = (pwdName) ? pwdName : (envUser) ? *envUser : (envLogname) ? *envLogname : "User"; + String defaultName = pwdName ? pwdName : envUser ? *envUser : envLogname ? *envLogname : "User"; #endif toml::table* general = root.insert("general", toml::table {}).first->second.as_table(); diff --git a/src/config/config.h b/src/config/config.h index af9f253..5eb5e15 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -21,7 +21,7 @@ struct General { DWORD size = sizeof(username); return GetUserNameA(username.data(), &size) ? username.data() : "User"; #else - if (struct passwd* pwd = getpwuid(getuid())) + if (const passwd* pwd = getpwuid(getuid())) return pwd->pw_name; if (Result envUser = GetEnv("USER")) @@ -55,7 +55,7 @@ struct Weather { static fn fromToml(const toml::table& tbl) -> Weather { Weather weather; - Option apiKey = tbl["api_key"].value(); + const Option apiKey = tbl["api_key"].value(); weather.enabled = tbl["enabled"].value_or(false) && apiKey; diff --git a/src/config/weather.cpp b/src/config/weather.cpp index a56d3b8..9ba93bb 100644 --- a/src/config/weather.cpp +++ b/src/config/weather.cpp @@ -39,7 +39,7 @@ namespace { DEBUG_LOG("Reading from cache file..."); try { - const String content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + const String content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); WeatherOutput result; if (const glz::error_ctx errc = glz::read(result, content); errc.ec != glz::error_code::none) diff --git a/src/core/system_data.cpp b/src/core/system_data.cpp new file mode 100644 index 0000000..94a24e6 --- /dev/null +++ b/src/core/system_data.cpp @@ -0,0 +1,64 @@ +#include + +#include "system_data.h" + +#include "src/config/config.h" +#include "src/os/os.h" + +namespace { + fn GetDate() -> String { + using namespace std::chrono; + + const year_month_day ymd = year_month_day { floor(system_clock::now()) }; + + String month = std::format("{:%B}", ymd); + + u32 day = static_cast(ymd.day()); + + CStr suffix = day >= 11 && day <= 13 ? "th" + : day % 10 == 1 ? "st" + : day % 10 == 2 ? "nd" + : day % 10 == 3 ? "rd" + : "th"; + + return std::format("{} {}{}", month, day, suffix); + } +} + +fn SystemData::fetchSystemData(const Config& config) -> SystemData { + SystemData data { + .date = GetDate(), + .host = os::GetHost(), + .kernel_version = os::GetKernelVersion(), + .os_version = os::GetOSVersion(), + .mem_info = os::GetMemInfo(), + .desktop_environment = os::GetDesktopEnvironment(), + .window_manager = os::GetWindowManager(), + }; + + auto diskShell = std::async(std::launch::async, [] { + auto [used, total] = os::GetDiskUsage(); + return std::make_tuple(used, total, os::GetShell()); + }); + + std::future weather; + std::future> nowPlaying; + + if (config.weather.enabled) + weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); }); + + if (config.now_playing.enabled) + nowPlaying = std::async(std::launch::async, os::GetNowPlaying); + + auto [used, total, shell] = diskShell.get(); + data.disk_used = used; + data.disk_total = total; + data.shell = shell; + + if (weather.valid()) + data.weather_info = weather.get(); + if (nowPlaying.valid()) + data.now_playing = nowPlaying.get(); + + return data; +} \ No newline at end of file diff --git a/src/core/system_data.h b/src/core/system_data.h new file mode 100644 index 0000000..ab360fc --- /dev/null +++ b/src/core/system_data.h @@ -0,0 +1,36 @@ +#pragma once + +#include "src/config/config.h" +#include "src/util/types.h" + +struct BytesToGiB { + u64 value; +}; + +constexpr u64 GIB = 1'073'741'824; + +template <> +struct std::formatter : std::formatter { + fn format(const BytesToGiB& BTG, auto& ctx) const { + return std::format_to(ctx.out(), "{:.2f}GiB", static_cast(BTG.value) / GIB); + } +}; + +// Structure to hold the collected system data +struct SystemData { + String date; + String host; + String kernel_version; + Result os_version; + Result mem_info; + Option desktop_environment; + String window_manager; + Option> now_playing; + Option weather_info; + u64 disk_used; + u64 disk_total; + String shell; + + // Static function to fetch the data + static fn fetchSystemData(const Config& config) -> SystemData; +}; diff --git a/src/main.cpp b/src/main.cpp index 592e3eb..e592a6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,24 +8,11 @@ #include #include "config/config.h" +#include "core/system_data.h" #include "os/os.h" constexpr inline bool SHOW_ICONS = true; -struct BytesToGiB { - u64 value; -}; - -// 1024^3 (size of 1 GiB) -constexpr u64 GIB = 1'073'741'824; - -template <> -struct std::formatter : std::formatter { - fn format(const BytesToGiB& BTG, auto& ctx) const { - return std::format_to(ctx.out(), "{:.2f}GiB", static_cast(BTG.value) / GIB); - } -}; - namespace ui { using ftxui::Color; @@ -99,93 +86,6 @@ namespace ui { } namespace { - template - fn visit_result(const Result& exp, ValueFunc value_func, ErrorFunc error_func) { - if (exp.has_value()) - return value_func(*exp); - - return error_func(exp.error()); - } - - fn GetDate() -> String { - using namespace std::chrono; - - const year_month_day ymd = year_month_day { floor(system_clock::now()) }; - - String month = std::format("{:%B}", ymd); - - u32 day = static_cast(ymd.day()); - - CStr suffix = static_cast( - (day >= 11 && day <= 13) ? "th" - : (day % 10 == 1) ? "st" - : (day % 10 == 2) ? "nd" - : (day % 10 == 3) ? "rd" - : "th" - ); - - return std::format("{} {}{}", month, day, suffix); - } - - struct SystemData { - String date; - String host; - String kernel_version; - Result os_version; - Result mem_info; - Option desktop_environment; - String window_manager; - Option> now_playing; - Option weather_info; - u64 disk_used; - u64 disk_total; - String shell; - - static fn fetchSystemData(const Config& config) -> SystemData { - SystemData data; - - // Single-threaded execution for core system info (faster on Windows) - data.date = GetDate(); - data.host = os::GetHost(); - data.kernel_version = os::GetKernelVersion(); - data.os_version = os::GetOSVersion(); - data.mem_info = os::GetMemInfo(); - - // Desktop environment info - data.desktop_environment = os::GetDesktopEnvironment(); - data.window_manager = os::GetWindowManager(); - - // Parallel execution for disk/shell only - auto diskShell = std::async(std::launch::async, [] { - auto [used, total] = os::GetDiskUsage(); - return std::make_tuple(used, total, os::GetShell()); - }); - - // Conditional tasks - std::future weather; - std::future> nowPlaying; - - if (config.weather.enabled) - weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); }); - - if (config.now_playing.enabled) - nowPlaying = std::async(std::launch::async, os::GetNowPlaying); - - // Get remaining results - auto [used, total, shell] = diskShell.get(); - data.disk_used = used; - data.disk_total = total; - data.shell = shell; - - if (weather.valid()) - data.weather_info = weather.get(); - if (nowPlaying.valid()) - data.now_playing = nowPlaying.get(); - - return data; - } - }; - using namespace ftxui; fn CreateColorCircles() -> Element { @@ -198,12 +98,12 @@ namespace { } fn SystemInfoBox(const Config& config, const SystemData& data) -> Element { - // Fetch data 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] = + // ReSharper disable once CppDFAUnreachableCode SHOW_ICONS ? ui::NERD_ICONS : ui::EMPTY_ICONS; Elements content; @@ -282,17 +182,15 @@ namespace { if (!data.kernel_version.empty()) content.push_back(createRow(kernelIcon, "Kernel", data.kernel_version)); - visit_result( - data.os_version, - [&](const String& version) { content.push_back(createRow(String(osIcon), "OS", version)); }, - [](const String& error) { ERROR_LOG("Failed to get OS version: {}", error); } - ); + 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()); - visit_result( - data.mem_info, - [&](const u64& mem) { content.push_back(createRow(memoryIcon, "RAM", std::format("{}", BytesToGiB { mem }))); }, - [](const String& error) { ERROR_LOG("Failed to get memory info: {}", error); } - ); + 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()); // Add Disk usage row content.push_back( @@ -303,14 +201,14 @@ namespace { content.push_back(separator() | color(ui::DEFAULT_THEME.border)); - if (data.desktop_environment.has_value() && *data.desktop_environment != data.window_manager) + if (data.desktop_environment && *data.desktop_environment != data.window_manager) content.push_back(createRow(deIcon, "DE", *data.desktop_environment)); if (!data.window_manager.empty()) content.push_back(createRow(wmIcon, "WM", data.window_manager)); // Now Playing row - if (nowPlayingEnabled && data.now_playing.has_value()) { + if (nowPlayingEnabled && data.now_playing) { if (const Result& nowPlayingResult = *data.now_playing; nowPlayingResult.has_value()) { const String& npText = *nowPlayingResult; diff --git a/src/os/linux.cpp b/src/os/linux.cpp index c0e15e7..06e07bb 100644 --- a/src/os/linux.cpp +++ b/src/os/linux.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -18,28 +19,39 @@ #include "src/util/macros.h" namespace fs = std::filesystem; +using namespace std::string_view_literals; namespace { using os::linux::DisplayGuard; using os::linux::WaylandDisplayGuard; + constexpr auto Trim(StringView sv) -> StringView { + using namespace std::ranges; + + constexpr auto isSpace = [](const char character) { return std::isspace(static_cast(character)); }; + + const borrowed_iterator_t start = find_if_not(sv, isSpace); + const borrowed_iterator_t> rstart = find_if_not(sv | views::reverse, isSpace); + + return sv.substr(start - sv.begin(), sv.size() - (rstart - sv.rbegin())); + } + fn GetX11WindowManager() -> String { - DisplayGuard display; + const DisplayGuard display; if (!display) return ""; - Atom supportingWmCheck = XInternAtom(display.get(), "_NET_SUPPORTING_WM_CHECK", False); - Atom wmName = XInternAtom(display.get(), "_NET_WM_NAME", False); - Atom utf8String = XInternAtom(display.get(), "UTF8_STRING", False); + const Atom supportingWmCheck = XInternAtom(display.get(), "_NET_SUPPORTING_WM_CHECK", False); + const Atom wmName = XInternAtom(display.get(), "_NET_WM_NAME", False); + const Atom utf8String = XInternAtom(display.get(), "UTF8_STRING", False); - Window root = display.defaultRootWindow(); + const Window root = display.defaultRootWindow(); - Window wmWindow = 0; - Atom actualType = 0; - i32 actualFormat = 0; - u64 nitems = 0, bytesAfter = 0; - u8* data = nullptr; + Atom actualType = 0; + i32 actualFormat = 0; + u64 nitems = 0, bytesAfter = 0; + u8* data = nullptr; if (XGetWindowProperty( display.get(), @@ -56,14 +68,13 @@ namespace { &data ) == Success && data) { - UniquePointer dataGuard(data, XFree); - wmWindow = *std::bit_cast(data); + const UniquePointer dataGuard(data, XFree); u8* nameData = nullptr; if (XGetWindowProperty( display.get(), - wmWindow, + *reinterpret_cast(data), wmName, 0, 1024, @@ -76,19 +87,18 @@ namespace { &nameData ) == Success && nameData) { - UniquePointer nameGuard(nameData, XFree); - return std::bit_cast(nameData); + const UniquePointer nameGuard(nameData, XFree); + return reinterpret_cast(nameData); } } return "Unknown (X11)"; } - fn ReadProcessCmdline(i32 pid) -> String { + fn ReadProcessCmdline(const i32 pid) -> String { std::ifstream cmdlineFile("/proc/" + std::to_string(pid) + "/cmdline"); - String cmdline; - if (getline(cmdlineFile, cmdline)) { + if (String cmdline; getline(cmdlineFile, cmdline)) { std::ranges::replace(cmdline, '\0', ' '); return cmdline; } @@ -96,75 +106,66 @@ namespace { return ""; } - fn DetectHyprlandSpecific() -> String { - Result xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP"); + fn DetectHyprlandSpecific() -> Option { + if (Result xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) { + std::ranges::transform(*xdgCurrentDesktop, xdgCurrentDesktop->begin(), tolower); - if (xdgCurrentDesktop) { - std::ranges::transform(*xdgCurrentDesktop, xdgCurrentDesktop->begin(), ::tolower); - - if (xdgCurrentDesktop->contains("hyprland")) + if (xdgCurrentDesktop->contains("hyprland"sv)) return "Hyprland"; } if (GetEnv("HYPRLAND_INSTANCE_SIGNATURE")) return "Hyprland"; - if (fs::exists("/run/user/" + std::to_string(getuid()) + "/hypr")) + if (fs::exists(std::format("/run/user/{}/hypr", getuid()))) return "Hyprland"; - return ""; + return None; } fn GetWaylandCompositor() -> String { - String hypr = DetectHyprlandSpecific(); + if (const Option hypr = DetectHyprlandSpecific()) + return *hypr; - if (!hypr.empty()) - return hypr; - - WaylandDisplayGuard display; + const WaylandDisplayGuard display; if (!display) return ""; - i32 fileDescriptor = display.fd(); + const i32 fileDescriptor = display.fd(); - struct ucred cred; - socklen_t len = sizeof(cred); + ucred cred; + u32 len = sizeof(cred); if (getsockopt(fileDescriptor, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) return ""; String compositorName; - String commPath = "/proc/" + std::to_string(cred.pid) + "/comm"; - std::ifstream commFile(commPath); - if (commFile >> compositorName) { - std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n'); + const String commPath = std::format("/proc/{}/comm", cred.pid); + if (std::ifstream commFile(commPath); commFile >> compositorName) { + const std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n'); compositorName.erase(removedRange.begin(), removedRange.end()); } - String cmdline = ReadProcessCmdline(cred.pid); - if (cmdline.contains("hyprland")) + if (const String cmdline = ReadProcessCmdline(cred.pid); cmdline.contains("hyprland"sv)) return "Hyprland"; - String exePath = "/proc/" + std::to_string(cred.pid) + "/exe"; + const String exePath = std::format("/proc/{}/exe", cred.pid); + Array buf; - ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1); - if (lenBuf != -1) { + if (const isize lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1); lenBuf != -1) { buf.at(static_cast(lenBuf)) = '\0'; - String exe(buf.data()); - if (exe.contains("hyprland")) + if (const String exe(buf.data()); exe.contains("hyprland"sv)) return "Hyprland"; } - return compositorName.contains("hyprland") ? "Hyprland" : compositorName; + return compositorName.contains("hyprland"sv) ? "Hyprland" : compositorName; } fn DetectFromEnvVars() -> Option { if (Result xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) { - const size_t colon = xdgCurrentDesktop->find(':'); - - if (colon != String::npos) + if (const usize colon = xdgCurrentDesktop->find(':'); colon != String::npos) return xdgCurrentDesktop->substr(0, colon); return *xdgCurrentDesktop; @@ -205,11 +206,11 @@ namespace { continue; String lowercaseStem = entry.path().stem().string(); - std::ranges::transform(lowercaseStem, lowercaseStem.begin(), ::tolower); + std::ranges::transform(lowercaseStem, lowercaseStem.begin(), tolower); - for (const Pair pattern : DE_PATTERNS) - if (pattern.first == lowercaseStem) - return String(pattern.second); + for (const auto [fst, snd] : DE_PATTERNS) + if (fst == lowercaseStem) + return String(snd); } } @@ -230,7 +231,7 @@ namespace { // clang-format on std::ifstream cmdline("/proc/self/environ"); - String envVars((std::istreambuf_iterator(cmdline)), std::istreambuf_iterator()); + const String envVars((std::istreambuf_iterator(cmdline)), std::istreambuf_iterator()); for (const auto& [process, deName] : processChecks) if (envVars.contains(process)) @@ -241,10 +242,10 @@ namespace { fn GetMprisPlayers(const SharedPointer& connection) -> Result, NowPlayingError> { try { - SharedPointer call = + const SharedPointer call = DBus::CallMessage::create("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"); - SharedPointer reply = connection->send_with_reply_blocking(call, 500); + const SharedPointer reply = connection->send_with_reply_blocking(call, 500); if (!reply) { ERROR_LOG("DBus timeout or null reply in ListNames"); @@ -257,7 +258,7 @@ namespace { Vec mprisPlayers; for (const String& name : allNamesStd) - if (StringView(name).contains("org.mpris.MediaPlayer2")) + if (StringView(name).contains("org.mpris.MediaPlayer2"sv)) mprisPlayers.emplace_back(name); return mprisPlayers; @@ -269,10 +270,9 @@ namespace { return Err(e.what()); } } - } -fn GetOSVersion() -> Result { +fn os::GetOSVersion() -> Result { constexpr CStr path = "/etc/os-release"; std::ifstream file(path); @@ -298,22 +298,22 @@ fn GetOSVersion() -> Result { return Err(std::format("PRETTY_NAME line not found in {}", path)); } -fn GetMemInfo() -> Result { +fn os::GetMemInfo() -> Result { struct sysinfo info; if (sysinfo(&info) != 0) return Err(std::format("sysinfo failed: {}", std::error_code(errno, std::generic_category()).message())); - return static_cast(info.totalram * info.mem_unit); + return info.totalram * info.mem_unit; } -fn GetNowPlaying() -> Result { +fn os::GetNowPlaying() -> Result { try { - SharedPointer dispatcher = DBus::StandaloneDispatcher::create(); + const SharedPointer dispatcher = DBus::StandaloneDispatcher::create(); if (!dispatcher) return Err("Failed to create DBus dispatcher"); - SharedPointer connection = dispatcher->create_connection(DBus::BusType::SESSION); + const SharedPointer connection = dispatcher->create_connection(DBus::BusType::SESSION); if (!connection) return Err("Failed to connect to session bus"); @@ -326,14 +326,14 @@ fn GetNowPlaying() -> Result { if (mprisPlayers.empty()) return Err(NowPlayingCode::NoPlayers); - String activePlayer = mprisPlayers.front(); + const String activePlayer = mprisPlayers.front(); - SharedPointer metadataCall = + const SharedPointer metadataCall = DBus::CallMessage::create(activePlayer, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); - (*metadataCall) << "org.mpris.MediaPlayer2.Player" << "Metadata"; + *metadataCall << "org.mpris.MediaPlayer2.Player" << "Metadata"; - SharedPointer metadataReply = connection->send_with_reply_blocking(metadataCall, 5000); + const SharedPointer metadataReply = connection->send_with_reply_blocking(metadataCall, 5000); String title; String artist; @@ -347,12 +347,11 @@ fn GetNowPlaying() -> Result { if (metadataVariant.type() == DBus::DataType::ARRAY) { Map metadata = metadataVariant.to_map(); - auto titleIter = metadata.find("xesam:title"); - if (titleIter != metadata.end() && titleIter->second.type() == DBus::DataType::STRING) + if (auto titleIter = metadata.find("xesam:title"); + titleIter != metadata.end() && titleIter->second.type() == DBus::DataType::STRING) title = titleIter->second.to_string(); - auto artistIter = metadata.find("xesam:artist"); - if (artistIter != metadata.end()) { + if (auto artistIter = metadata.find("xesam:artist"); artistIter != metadata.end()) { if (artistIter->second.type() == DBus::DataType::ARRAY) { if (Vec artists = artistIter->second.to_vector(); !artists.empty()) artist = artists[0]; @@ -369,24 +368,24 @@ fn GetNowPlaying() -> Result { } catch (const Exception& e) { ERROR_LOG("Error processing metadata reply: {}", e.what()); } } - return std::format("{}{}{}", artist, (!artist.empty() && !title.empty()) ? " - " : "", title); + return std::format("{}{}{}", artist, !artist.empty() && !title.empty() ? " - " : "", title); } catch (const DBus::Error& e) { return Err(std::format("DBus error: {}", e.what())); } catch (const Exception& e) { return Err(std::format("General error: {}", e.what())); } } -fn GetWindowManager() -> String { +fn os::GetWindowManager() -> String { const Result waylandDisplay = GetEnv("WAYLAND_DISPLAY"); - const Result xdgSessionType = GetEnv("XDG_SESSION_TYPE"); - if (waylandDisplay || (xdgSessionType && xdgSessionType->contains("wayland"))) { + if (const Result xdgSessionType = GetEnv("XDG_SESSION_TYPE"); + waylandDisplay || (xdgSessionType && xdgSessionType->contains("wayland"sv))) { String compositor = GetWaylandCompositor(); if (!compositor.empty()) return compositor; if (const Result xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) { - std::ranges::transform(compositor, compositor.begin(), ::tolower); - if (xdgCurrentDesktop->contains("hyprland")) + std::ranges::transform(compositor, compositor.begin(), tolower); + if (xdgCurrentDesktop->contains("hyprland"sv)) return "Hyprland"; } } @@ -397,7 +396,7 @@ fn GetWindowManager() -> String { return "Unknown"; } -fn GetDesktopEnvironment() -> Option { +fn os::GetDesktopEnvironment() -> Option { if (Option desktopEnvironment = DetectFromEnvVars()) return desktopEnvironment; @@ -407,19 +406,21 @@ fn GetDesktopEnvironment() -> Option { return DetectFromProcesses(); } -fn GetShell() -> String { - const Vec> shellMap { - { "bash", "Bash" }, - { "zsh", "Zsh" }, - { "fish", "Fish" }, - { "nu", "Nushell" }, - { "sh", "SH" }, // sh last because other shells contain "sh" - }; - +fn os::GetShell() -> String { if (const Result shellPath = GetEnv("SHELL")) { - for (const auto& shellPair : shellMap) - if (shellPath->contains(shellPair.first)) - return shellPair.second; + // clang-format off + constexpr Array, 5> shellMap {{ + { "bash", "Bash" }, + { "zsh", "Zsh" }, + { "fish", "Fish" }, + { "nu", "Nushell" }, + { "sh", "SH" }, // sh last because other shells contain "sh" + }}; + // clang-format on + + for (const auto& [exe, name] : shellMap) + if (shellPath->contains(exe)) + return String(name); return *shellPath; // fallback to the raw shell path } @@ -427,7 +428,7 @@ fn GetShell() -> String { return ""; } -fn GetHost() -> String { +fn os::GetHost() -> String { constexpr CStr path = "/sys/class/dmi/id/product_family"; std::ifstream file(path); @@ -444,21 +445,21 @@ fn GetHost() -> String { return ""; } - return productFamily.erase(productFamily.find_last_not_of(" \t\n\r") + 1); + return String(Trim(productFamily)); } -fn GetKernelVersion() -> String { - struct utsname uts; +fn os::GetKernelVersion() -> String { + utsname uts; if (uname(&uts) == -1) { ERROR_LOG("uname() failed: {}", std::error_code(errno, std::generic_category()).message()); return ""; } - return static_cast(uts.release); + return uts.release; } -fn GetDiskUsage() -> Pair { +fn os::GetDiskUsage() -> Pair { struct statvfs stat; if (statvfs("/", &stat) == -1) { @@ -466,7 +467,9 @@ fn GetDiskUsage() -> Pair { return { 0, 0 }; } + // ReSharper disable CppRedundantParentheses return { (stat.f_blocks * stat.f_frsize) - (stat.f_bfree * stat.f_frsize), stat.f_blocks * stat.f_frsize }; + // ReSharper restore CppRedundantParentheses } #endif diff --git a/src/os/linux/display_guards.cpp b/src/os/linux/display_guards.cpp index 3b523a1..9a25635 100644 --- a/src/os/linux/display_guards.cpp +++ b/src/os/linux/display_guards.cpp @@ -1,13 +1,13 @@ #ifdef __linux__ -#include // for std::exchange +#include #include "display_guards.h" #include "src/util/macros.h" namespace os::linux { - DisplayGuard::DisplayGuard(CStr name) : m_Display(XOpenDisplay(name)) {} + DisplayGuard::DisplayGuard(const CStr name) : m_Display(XOpenDisplay(name)) {} DisplayGuard::~DisplayGuard() { if (m_Display) diff --git a/src/os/linux/display_guards.h b/src/os/linux/display_guards.h index f47198f..6e97b6e 100644 --- a/src/os/linux/display_guards.h +++ b/src/os/linux/display_guards.h @@ -13,7 +13,6 @@ namespace os::linux { * Automatically handles resource acquisition and cleanup */ class DisplayGuard { - private: Display* m_Display; public: @@ -32,9 +31,9 @@ namespace os::linux { DisplayGuard(DisplayGuard&& other) noexcept; fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard&; - [[nodiscard]] operator bool() const; - [[nodiscard]] fn get() const -> Display*; - [[nodiscard]] fn defaultRootWindow() const -> Window; + [[nodiscard]] explicit operator bool() const; + [[nodiscard]] fn get() const -> Display*; + [[nodiscard]] fn defaultRootWindow() const -> Window; }; /** @@ -42,7 +41,6 @@ namespace os::linux { * Automatically handles resource acquisition and cleanup */ class WaylandDisplayGuard { - private: wl_display* m_Display; public: @@ -60,9 +58,9 @@ namespace os::linux { WaylandDisplayGuard(WaylandDisplayGuard&& other) noexcept; fn operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard&; - [[nodiscard]] operator bool() const; - [[nodiscard]] fn get() const -> wl_display*; - [[nodiscard]] fn fd() const -> i32; + [[nodiscard]] explicit operator bool() const; + [[nodiscard]] fn get() const -> wl_display*; + [[nodiscard]] fn fd() const -> i32; }; } diff --git a/src/util/macros.h b/src/util/macros.h index 0c3c23a..0c51cc2 100644 --- a/src/util/macros.h +++ b/src/util/macros.h @@ -121,8 +121,7 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st -> void { using namespace std::chrono; - const time_point>> now = - std::chrono::floor(std::chrono::system_clock::now()); + const time_point> now = std::chrono::floor(system_clock::now()); const auto [color, levelStr] = [&] { switch (level) { diff --git a/src/util/types.h b/src/util/types.h index 71d5800..c83fd7d 100644 --- a/src/util/types.h +++ b/src/util/types.h @@ -259,12 +259,12 @@ using NowPlayingError = std::variant< enum class EnvError : u8 { NotFound, AccessError }; -inline auto GetEnv(const String& name) -> Result { +inline auto GetEnv(CStr name) -> Result { #ifdef _WIN32 char* rawPtr = nullptr; usize bufferSize = 0; - const i32 err = _dupenv_s(&rawPtr, &bufferSize, name.c_str()); + const i32 err = _dupenv_s(&rawPtr, &bufferSize, name); const UniquePointer ptrManager(rawPtr, free); @@ -276,7 +276,7 @@ inline auto GetEnv(const String& name) -> Result { return ptrManager.get(); #else - CStr value = std::getenv(name.c_str()); + const CStr value = std::getenv(name); if (!value) return Err(EnvError::NotFound);