From e4cafe1960de13b8af8ac09e4edd10945b31c886 Mon Sep 17 00:00:00 2001 From: pupbrained Date: Sat, 3 May 2025 22:23:11 -0400 Subject: [PATCH] serenityos support yayyy --- meson.build | 27 +++---- src/config/weather.cpp | 3 +- src/main.cpp | 42 ++++------- src/os/serenity.cpp | 157 +++++++++++++++++++++++++++++++++++++++++ src/os/shared.cpp | 22 +++--- src/util/logging.hpp | 20 ++++-- 6 files changed, 214 insertions(+), 57 deletions(-) create mode 100644 src/os/serenity.cpp diff --git a/meson.build b/meson.build index 706a75c..fccf1cd 100644 --- a/meson.build +++ b/meson.build @@ -7,10 +7,10 @@ project( version : '0.1.0', default_options : [ 'default_library=static', - 'buildtype=release', + 'buildtype=debugoptimized', 'b_vscrt=mt', 'b_lto=true', - 'b_ndebug=false', + 'b_ndebug=if-release', 'warning_level=3', ], ) @@ -75,9 +75,11 @@ if cpp.get_id() in ['msvc', 'clang-cl'] common_cpp_args += common_warning_flags + common_cpp_flags['common'] + ['-fcolor-diagnostics', '-fdiagnostics-format=clang'] endif else - common_cpp_args = common_warning_flags + common_cpp_flags['common'] + common_cpp_flags['unix_extra'] + common_cpp_args = common_warning_flags + common_cpp_flags['common'] if host_system == 'windows' common_cpp_args += common_cpp_flags['windows_extra'] + elif host_system != 'serenity' + common_cpp_args += common_cpp_flags['unix_extra'] endif endif @@ -95,6 +97,7 @@ platform_sources = { 'dragonfly' : ['src/os/bsd.cpp'], 'haiku' : ['src/os/haiku.cpp'], 'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'], + 'serenity' : ['src/os/serenity.cpp'], 'windows' : ['src/os/windows.cpp'], } @@ -127,15 +130,15 @@ elif host_system == 'windows' cpp.find_library('dwmapi'), cpp.find_library('windowsapp'), ] -# elif host_system == 'linux' or host_system == 'freebsd' or host_system == 'netbsd' or host_system == 'dragonfly' or host_system == 'haiku' -# platform_deps += [ -# dependency('SQLiteCpp'), -# dependency('xcb'), -# dependency('xau'), -# dependency('xdmcp'), -# dependency('wayland-client'), -# dependency('dbus-1'), -# ] +elif host_system != 'serenity' + platform_deps += [ + dependency('SQLiteCpp'), + dependency('xcb'), + dependency('xau'), + dependency('xdmcp'), + dependency('wayland-client'), + dependency('dbus-1'), + ] endif # FTXUI configuration diff --git a/src/config/weather.cpp b/src/config/weather.cpp index e8fe108..3e3df0a 100644 --- a/src/config/weather.cpp +++ b/src/config/weather.cpp @@ -15,7 +15,6 @@ #include // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read #include // std::ios::{binary, trunc} #include // std::istreambuf_iterator -#include // std::move #include // std::{get, holds_alternative} #include "src/util/cache.hpp" @@ -70,7 +69,7 @@ namespace { if (const error_ctx errc = read(output, responseBuffer); errc.ec != error_code::none) return Err("API response parse error: " + format_error(errc, responseBuffer)); - return std::move(output); + return output; } } // namespace diff --git a/src/main.cpp b/src/main.cpp index b378c3d..41dac0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,7 @@ #include // ftxui::Color #include // ftxui::{Screen, Dimension::Full} #include // ftxui::string_width -#include +#include // std::cout #include // std::ranges::{iota, to, transform} #include "src/config/config.hpp" @@ -116,10 +116,13 @@ namespace { }; fn CreateColorCircles() -> Element { - fn color_view = std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) { - return ftxui::hbox({ ftxui::text("◯") | ftxui::bold | ftxui::color(static_cast(colorIndex)), - ftxui::text(" ") }); - }); + fn color_view = + std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) { + return ftxui::hbox( + { ftxui::text("◯") | ftxui::bold | ftxui::color(static_cast(colorIndex)), + ftxui::text(" ") } + ); + }); Elements elements_container(std::ranges::begin(color_view), std::ranges::end(color_view)); @@ -149,8 +152,6 @@ namespace { if (data.date) initialRows.push_back({ calendarIcon, "Date", *data.date }); - else - debug_at(data.date.error()); if (weather.enabled && data.weather) { const weather::Output& weatherInfo = *data.weather; @@ -158,27 +159,20 @@ namespace { ? std::format("{}°F in {}", std::lround(weatherInfo.main.temp), weatherInfo.name) : std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description); initialRows.push_back({ weatherIcon, "Weather", std::move(weatherValue) }); - } else if (weather.enabled) - debug_at(data.weather.error()); + } if (data.host && !data.host->empty()) systemInfoRows.push_back({ hostIcon, "Host", *data.host }); - else - debug_at(data.host.error()); if (data.osVersion) systemInfoRows.push_back({ osIcon, "OS", *data.osVersion }); - else - debug_at(data.osVersion.error()); if (data.kernelVersion) systemInfoRows.push_back({ kernelIcon, "Kernel", *data.kernelVersion }); - else - debug_at(data.kernelVersion.error()); if (data.memInfo) systemInfoRows.push_back({ memoryIcon, "RAM", std::format("{}", BytesToGiB { *data.memInfo }) }); - else + else if (!data.memInfo.has_value()) debug_at(data.memInfo.error()); if (data.diskUsage) @@ -187,34 +181,27 @@ namespace { "Disk", std::format("{}/{}", BytesToGiB { data.diskUsage->used_bytes }, BytesToGiB { data.diskUsage->total_bytes }) } ); - else - debug_at(data.diskUsage.error()); if (data.shell) systemInfoRows.push_back({ shellIcon, "Shell", *data.shell }); - else - debug_at(data.shell.error()); if (data.packageCount) { if (*data.packageCount > 0) systemInfoRows.push_back({ packageIcon, "Packages", std::format("{}", *data.packageCount) }); else debug_log("Package count is 0, skipping"); - } else - debug_at(data.packageCount.error()); + } bool addedDe = false; if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) { envInfoRows.push_back({ deIcon, "DE", *data.desktopEnv }); addedDe = true; - } else if (!data.desktopEnv) - debug_at(data.desktopEnv.error()); + } if (data.windowMgr) { if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr)) envInfoRows.push_back({ wmIcon, "WM", *data.windowMgr }); - } else - debug_at(data.windowMgr.error()); + } bool nowPlayingActive = false; String npText; @@ -224,8 +211,7 @@ namespace { const String artist = data.nowPlaying->artist.value_or("Unknown Artist"); npText = artist + " - " + title; nowPlayingActive = true; - } else if (config.nowPlaying.enabled) - debug_at(data.nowPlaying.error()); + } usize maxContentWidth = 0; diff --git a/src/os/serenity.cpp b/src/os/serenity.cpp new file mode 100644 index 0000000..662258b --- /dev/null +++ b/src/os/serenity.cpp @@ -0,0 +1,157 @@ +#ifdef __serenity__ + +// clang-format off +#include // For errno +#include // For strerror +#include +#include +#include +#include // glz::object +#include // glz::error_ctx, glz::error_code +#include // glz::detail::Object +#include // glz::read_json +#include +#include // For getpwuid(), struct passwd +#include +#include +#include +#include // For uid_t +#include +#include + +#include "src/util/defs.hpp" +#include "src/util/error.hpp" +#include "src/util/helpers.hpp" +#include "src/util/logging.hpp" +#include "src/util/types.hpp" + +#include "os.hpp" +// clang-format on + +using namespace util::types; +using util::error::DracError, util::error::DracErrorCode; +using util::helpers::GetEnv; + +namespace { + using glz::opts, glz::detail::Object, glz::object; + + constexpr opts glaze_opts = { .error_on_unknown_keys = false }; + + struct MemStatData { + u64 physical_allocated = 0; + u64 physical_available = 0; + + // NOLINTBEGIN(readability-identifier-naming) + struct glaze { + using T = MemStatData; + static constexpr Object value = + object("physical_allocated", &T::physical_allocated, "physical_available", &T::physical_available); + }; + // NOLINTEND(readability-identifier-naming) + }; +} // namespace + +namespace os { + fn GetOSVersion() -> Result { + utsname uts; + + if (uname(&uts) == -1) + return Err(DracError::withErrno("uname call failed for OS Version")); + + return uts.sysname; + } + + fn GetMemInfo() -> Result { + CStr path = "/sys/kernel/memstat"; + std::ifstream file(path); + + if (!file) + return Err(DracError(DracErrorCode::NotFound, std::format("Could not open {}", path))); + + String buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + if (buffer.empty()) + return Err(DracError(DracErrorCode::IoError, std::format("File is empty: {}", path))); + + MemStatData data; + + glz::error_ctx error_context = glz::read(data, buffer); + + if (error_context) + return Err(DracError( + DracErrorCode::ParseError, + std::format("Failed to parse JSON from {}: {}", path, glz::format_error(error_context, buffer)) + )); + + if (data.physical_allocated > std::numeric_limits::max() - data.physical_available) + return Err(DracError(DracErrorCode::InternalError, "Memory size overflow during calculation")); + + return (data.physical_allocated + data.physical_available) * PAGE_SIZE; + } + + fn GetNowPlaying() -> Result { + return Err(DracError(DracErrorCode::NotSupported, "Now playing is not supported on SerenityOS")); + } + + fn GetWindowManager() -> Result { return "WindowManager"; } + + fn GetDesktopEnvironment() -> Result { return "SerenityOS Desktop"; } + + fn GetShell() -> Result { + uid_t userId = getuid(); + errno = 0; + struct passwd* pw = getpwuid(userId); + + if (pw == nullptr) + return Err(DracError(DracErrorCode::NotFound, std::format("User ID {} not found in /etc/passwd", userId))); + + if (pw->pw_shell == nullptr || *(pw->pw_shell) == '\0') + return Err(DracError( + DracErrorCode::NotFound, std::format("User shell entry is empty in /etc/passwd for user ID {}", userId) + )); + + String shell = pw->pw_shell; + + if (shell.starts_with("/bin/")) + shell = shell.substr(5); + + return shell; + } + + fn GetHost() -> Result { + Array hostname_buffer; + + if (gethostname(hostname_buffer.data(), hostname_buffer.size()) != 0) + return Err(DracError::withErrno("gethostname() failed: {}")); + + return String(hostname_buffer.data()); + } + + fn GetKernelVersion() -> Result { + utsname uts; + + if (uname(&uts) == -1) + return Err(DracError::withErrno("uname call failed for Kernel Version")); + + return uts.release; + } + + fn GetDiskUsage() -> Result { + struct statvfs stat; + if (statvfs("/", &stat) == -1) + return Err(DracError::withErrno("statvfs call failed for '/'")); + + const u64 total_bytes = static_cast(stat.f_blocks) * stat.f_frsize; + const u64 free_bytes = static_cast(stat.f_bfree) * stat.f_frsize; + const u64 used_bytes = total_bytes - free_bytes; + + return DiskSpace { .used_bytes = used_bytes, .total_bytes = total_bytes }; + } + + fn GetPackageCount() -> Result { + return Err(DracError(DracErrorCode::NotSupported, "Package count is not supported on SerenityOS")); + } +} // namespace os + +#endif // __serenity__ diff --git a/src/os/shared.cpp b/src/os/shared.cpp index 7a913b9..e6874e1 100644 --- a/src/os/shared.cpp +++ b/src/os/shared.cpp @@ -1,3 +1,6 @@ +#ifndef __serenity__ + +// clang-format off #ifndef _WIN32 #include // SQLite::{Database, OPEN_READONLY} #include // SQLite::Exception @@ -22,6 +25,7 @@ #include "src/util/types.hpp" #include "os.hpp" +// clang-format on using util::error::DracError, util::error::DracErrorCode; using util::types::u64, util::types::i64, util::types::String, util::types::StringView, util::types::Result, @@ -33,13 +37,13 @@ namespace { using namespace std::chrono; using namespace util::cache; -#ifndef _WIN32 + #ifndef _WIN32 struct PackageManagerInfo { String id; fs::path dbPath; String countQuery; }; -#endif + #endif struct PkgCountCacheData { u64 count {}; @@ -55,7 +59,7 @@ namespace { // NOLINTEND(readability-identifier-naming) }; -#ifndef _WIN32 + #ifndef _WIN32 fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result { const auto& [pmId, dbPath, countQuery] = pmInfo; @@ -117,9 +121,9 @@ namespace { return count; } -#endif + #endif -#ifndef _WIN32 + #ifndef _WIN32 fn GetNixPackageCount() -> Result { debug_log("Attempting to get Nix package count."); @@ -140,7 +144,7 @@ namespace { return GetPackageCountInternalDb(nixInfo); } -#endif + #endif fn GetCargoPackageCount() -> Result { using util::helpers::GetEnv; @@ -171,12 +175,12 @@ namespace os::shared { fn GetPackageCount() -> Result { u64 count = 0; -#ifndef _WIN32 + #ifndef _WIN32 if (const Result pkgCount = GetNixPackageCount()) count += *pkgCount; else debug_at(pkgCount.error()); -#endif + #endif if (const Result pkgCount = GetCargoPackageCount()) count += *pkgCount; @@ -186,3 +190,5 @@ namespace os::shared { return count; } } // namespace os::shared + +#endif // !__serenity__ diff --git a/src/util/logging.hpp b/src/util/logging.hpp index 3a9a6cb..b00abd3 100644 --- a/src/util/logging.hpp +++ b/src/util/logging.hpp @@ -1,11 +1,12 @@ #pragma once #include // std::chrono::{days, floor, seconds, system_clock} +#include // localtime_r/s, strftime, time_t, tm #include // std::filesystem::path #include // std::format #include // ftxui::Color -#include // std::{mutex, lock_guard} #include // std::cout +#include // std::{mutex, lock_guard} #include // std::forward #ifndef NDEBUG @@ -169,7 +170,7 @@ namespace util::logging { const auto nowTp = system_clock::now(); const std::time_t nowTt = system_clock::to_time_t(nowTp); - std::tm localTm; + std::tm localTm {}; String timestamp; @@ -179,13 +180,19 @@ namespace util::logging { if (localtime_r(&nowTt, &localTm) != nullptr) { #endif Array timeBuffer {}; + auto formattedTime = + std::strftime(timeBuffer.data(), sizeof(timeBuffer), LogLevelConst::TIMESTAMP_FORMAT, &localTm); - if (std::strftime(timeBuffer.data(), sizeof(timeBuffer), LogLevelConst::TIMESTAMP_FORMAT, &localTm) > 0) + if (formattedTime > 0) { timestamp = timeBuffer.data(); - else - timestamp = "??:??:?? (strf_err)"; - } else + } else { + try { + timestamp = std::format("{:%X}", nowTp); + } catch (const std::format_error& fmt_err) { timestamp = "??:??:?? (fmt_err)"; } + } + } else { timestamp = "??:??:?? (conv_err)"; + } const String message = std::format(fmt, std::forward(args)...); @@ -202,7 +209,6 @@ namespace util::logging { const String fileLine = std::format(LogLevelConst::FILE_LINE_FORMAT, path(loc.file_name()).lexically_normal().string(), loc.line()); const String fullDebugLine = std::format("{}{}", LogLevelConst::DEBUG_LINE_PREFIX, fileLine); - std::cout << '\n' << Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR)); #endif