serenityos support yayyy

This commit is contained in:
Mars 2025-05-03 22:23:11 -04:00
parent 735e3fb56f
commit e4cafe1960
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
6 changed files with 214 additions and 57 deletions

View file

@ -7,10 +7,10 @@ project(
version : '0.1.0', version : '0.1.0',
default_options : [ default_options : [
'default_library=static', 'default_library=static',
'buildtype=release', 'buildtype=debugoptimized',
'b_vscrt=mt', 'b_vscrt=mt',
'b_lto=true', 'b_lto=true',
'b_ndebug=false', 'b_ndebug=if-release',
'warning_level=3', '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'] common_cpp_args += common_warning_flags + common_cpp_flags['common'] + ['-fcolor-diagnostics', '-fdiagnostics-format=clang']
endif endif
else 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' if host_system == 'windows'
common_cpp_args += common_cpp_flags['windows_extra'] common_cpp_args += common_cpp_flags['windows_extra']
elif host_system != 'serenity'
common_cpp_args += common_cpp_flags['unix_extra']
endif endif
endif endif
@ -95,6 +97,7 @@ platform_sources = {
'dragonfly' : ['src/os/bsd.cpp'], 'dragonfly' : ['src/os/bsd.cpp'],
'haiku' : ['src/os/haiku.cpp'], 'haiku' : ['src/os/haiku.cpp'],
'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'], 'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'],
'serenity' : ['src/os/serenity.cpp'],
'windows' : ['src/os/windows.cpp'], 'windows' : ['src/os/windows.cpp'],
} }
@ -127,15 +130,15 @@ elif host_system == 'windows'
cpp.find_library('dwmapi'), cpp.find_library('dwmapi'),
cpp.find_library('windowsapp'), cpp.find_library('windowsapp'),
] ]
# elif host_system == 'linux' or host_system == 'freebsd' or host_system == 'netbsd' or host_system == 'dragonfly' or host_system == 'haiku' elif host_system != 'serenity'
# platform_deps += [ platform_deps += [
# dependency('SQLiteCpp'), dependency('SQLiteCpp'),
# dependency('xcb'), dependency('xcb'),
# dependency('xau'), dependency('xau'),
# dependency('xdmcp'), dependency('xdmcp'),
# dependency('wayland-client'), dependency('wayland-client'),
# dependency('dbus-1'), dependency('dbus-1'),
# ] ]
endif endif
# FTXUI configuration # FTXUI configuration

View file

@ -15,7 +15,6 @@
#include <glaze/json/read.hpp> // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read<glz::opts> #include <glaze/json/read.hpp> // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read<glz::opts>
#include <ios> // std::ios::{binary, trunc} #include <ios> // std::ios::{binary, trunc}
#include <iterator> // std::istreambuf_iterator #include <iterator> // std::istreambuf_iterator
#include <utility> // std::move
#include <variant> // std::{get, holds_alternative} #include <variant> // std::{get, holds_alternative}
#include "src/util/cache.hpp" #include "src/util/cache.hpp"
@ -70,7 +69,7 @@ namespace {
if (const error_ctx errc = read<glaze_opts>(output, responseBuffer); errc.ec != error_code::none) if (const error_ctx errc = read<glaze_opts>(output, responseBuffer); errc.ec != error_code::none)
return Err("API response parse error: " + format_error(errc, responseBuffer)); return Err("API response parse error: " + format_error(errc, responseBuffer));
return std::move(output); return output;
} }
} // namespace } // namespace

View file

@ -5,7 +5,7 @@
#include <ftxui/screen/color.hpp> // ftxui::Color #include <ftxui/screen/color.hpp> // ftxui::Color
#include <ftxui/screen/screen.hpp> // ftxui::{Screen, Dimension::Full} #include <ftxui/screen/screen.hpp> // ftxui::{Screen, Dimension::Full}
#include <ftxui/screen/string.hpp> // ftxui::string_width #include <ftxui/screen/string.hpp> // ftxui::string_width
#include <iostream> #include <iostream> // std::cout
#include <ranges> // std::ranges::{iota, to, transform} #include <ranges> // std::ranges::{iota, to, transform}
#include "src/config/config.hpp" #include "src/config/config.hpp"
@ -116,10 +116,13 @@ namespace {
}; };
fn CreateColorCircles() -> Element { fn CreateColorCircles() -> Element {
fn color_view = std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) { fn color_view =
return ftxui::hbox({ ftxui::text("") | ftxui::bold | ftxui::color(static_cast<ftxui::Color::Palette256>(colorIndex)), std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) {
ftxui::text(" ") }); return ftxui::hbox(
}); { ftxui::text("") | ftxui::bold | ftxui::color(static_cast<ftxui::Color::Palette256>(colorIndex)),
ftxui::text(" ") }
);
});
Elements elements_container(std::ranges::begin(color_view), std::ranges::end(color_view)); Elements elements_container(std::ranges::begin(color_view), std::ranges::end(color_view));
@ -149,8 +152,6 @@ namespace {
if (data.date) if (data.date)
initialRows.push_back({ calendarIcon, "Date", *data.date }); initialRows.push_back({ calendarIcon, "Date", *data.date });
else
debug_at(data.date.error());
if (weather.enabled && data.weather) { if (weather.enabled && data.weather) {
const weather::Output& weatherInfo = *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 in {}", std::lround(weatherInfo.main.temp), weatherInfo.name)
: std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description); : std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description);
initialRows.push_back({ weatherIcon, "Weather", std::move(weatherValue) }); initialRows.push_back({ weatherIcon, "Weather", std::move(weatherValue) });
} else if (weather.enabled) }
debug_at(data.weather.error());
if (data.host && !data.host->empty()) if (data.host && !data.host->empty())
systemInfoRows.push_back({ hostIcon, "Host", *data.host }); systemInfoRows.push_back({ hostIcon, "Host", *data.host });
else
debug_at(data.host.error());
if (data.osVersion) if (data.osVersion)
systemInfoRows.push_back({ osIcon, "OS", *data.osVersion }); systemInfoRows.push_back({ osIcon, "OS", *data.osVersion });
else
debug_at(data.osVersion.error());
if (data.kernelVersion) if (data.kernelVersion)
systemInfoRows.push_back({ kernelIcon, "Kernel", *data.kernelVersion }); systemInfoRows.push_back({ kernelIcon, "Kernel", *data.kernelVersion });
else
debug_at(data.kernelVersion.error());
if (data.memInfo) if (data.memInfo)
systemInfoRows.push_back({ memoryIcon, "RAM", std::format("{}", BytesToGiB { *data.memInfo }) }); systemInfoRows.push_back({ memoryIcon, "RAM", std::format("{}", BytesToGiB { *data.memInfo }) });
else else if (!data.memInfo.has_value())
debug_at(data.memInfo.error()); debug_at(data.memInfo.error());
if (data.diskUsage) if (data.diskUsage)
@ -187,34 +181,27 @@ namespace {
"Disk", "Disk",
std::format("{}/{}", BytesToGiB { data.diskUsage->used_bytes }, BytesToGiB { data.diskUsage->total_bytes }) } std::format("{}/{}", BytesToGiB { data.diskUsage->used_bytes }, BytesToGiB { data.diskUsage->total_bytes }) }
); );
else
debug_at(data.diskUsage.error());
if (data.shell) if (data.shell)
systemInfoRows.push_back({ shellIcon, "Shell", *data.shell }); systemInfoRows.push_back({ shellIcon, "Shell", *data.shell });
else
debug_at(data.shell.error());
if (data.packageCount) { if (data.packageCount) {
if (*data.packageCount > 0) if (*data.packageCount > 0)
systemInfoRows.push_back({ packageIcon, "Packages", std::format("{}", *data.packageCount) }); systemInfoRows.push_back({ packageIcon, "Packages", std::format("{}", *data.packageCount) });
else else
debug_log("Package count is 0, skipping"); debug_log("Package count is 0, skipping");
} else }
debug_at(data.packageCount.error());
bool addedDe = false; bool addedDe = false;
if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) { if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) {
envInfoRows.push_back({ deIcon, "DE", *data.desktopEnv }); envInfoRows.push_back({ deIcon, "DE", *data.desktopEnv });
addedDe = true; addedDe = true;
} else if (!data.desktopEnv) }
debug_at(data.desktopEnv.error());
if (data.windowMgr) { if (data.windowMgr) {
if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr)) if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr))
envInfoRows.push_back({ wmIcon, "WM", *data.windowMgr }); envInfoRows.push_back({ wmIcon, "WM", *data.windowMgr });
} else }
debug_at(data.windowMgr.error());
bool nowPlayingActive = false; bool nowPlayingActive = false;
String npText; String npText;
@ -224,8 +211,7 @@ namespace {
const String artist = data.nowPlaying->artist.value_or("Unknown Artist"); const String artist = data.nowPlaying->artist.value_or("Unknown Artist");
npText = artist + " - " + title; npText = artist + " - " + title;
nowPlayingActive = true; nowPlayingActive = true;
} else if (config.nowPlaying.enabled) }
debug_at(data.nowPlaying.error());
usize maxContentWidth = 0; usize maxContentWidth = 0;

157
src/os/serenity.cpp Normal file
View file

@ -0,0 +1,157 @@
#ifdef __serenity__
// clang-format off
#include <cerrno> // For errno
#include <cstring> // For strerror
#include <filesystem>
#include <format>
#include <fstream>
#include <glaze/core/common.hpp> // glz::object
#include <glaze/core/context.hpp> // glz::error_ctx, glz::error_code
#include <glaze/core/meta.hpp> // glz::detail::Object
#include <glaze/json/read.hpp> // glz::read_json
#include <iostream>
#include <pwd.h> // For getpwuid(), struct passwd
#include <string>
#include <string_view>
#include <sys/statvfs.h>
#include <sys/types.h> // For uid_t
#include <sys/utsname.h>
#include <unistd.h>
#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<String, DracError> {
utsname uts;
if (uname(&uts) == -1)
return Err(DracError::withErrno("uname call failed for OS Version"));
return uts.sysname;
}
fn GetMemInfo() -> Result<u64, DracError> {
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<char>(file)), std::istreambuf_iterator<char>());
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<glaze_opts>(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<u64>::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<MediaInfo, DracError> {
return Err(DracError(DracErrorCode::NotSupported, "Now playing is not supported on SerenityOS"));
}
fn GetWindowManager() -> Result<String, DracError> { return "WindowManager"; }
fn GetDesktopEnvironment() -> Result<String, DracError> { return "SerenityOS Desktop"; }
fn GetShell() -> Result<String, DracError> {
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<String, DracError> {
Array<char, HOST_NAME_MAX> 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<String, DracError> {
utsname uts;
if (uname(&uts) == -1)
return Err(DracError::withErrno("uname call failed for Kernel Version"));
return uts.release;
}
fn GetDiskUsage() -> Result<DiskSpace, DracError> {
struct statvfs stat;
if (statvfs("/", &stat) == -1)
return Err(DracError::withErrno("statvfs call failed for '/'"));
const u64 total_bytes = static_cast<u64>(stat.f_blocks) * stat.f_frsize;
const u64 free_bytes = static_cast<u64>(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<u64, DracError> {
return Err(DracError(DracErrorCode::NotSupported, "Package count is not supported on SerenityOS"));
}
} // namespace os
#endif // __serenity__

View file

@ -1,3 +1,6 @@
#ifndef __serenity__
// clang-format off
#ifndef _WIN32 #ifndef _WIN32
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY} #include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
#include <SQLiteCpp/Exception.h> // SQLite::Exception #include <SQLiteCpp/Exception.h> // SQLite::Exception
@ -22,6 +25,7 @@
#include "src/util/types.hpp" #include "src/util/types.hpp"
#include "os.hpp" #include "os.hpp"
// clang-format on
using util::error::DracError, util::error::DracErrorCode; using util::error::DracError, util::error::DracErrorCode;
using util::types::u64, util::types::i64, util::types::String, util::types::StringView, util::types::Result, 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 std::chrono;
using namespace util::cache; using namespace util::cache;
#ifndef _WIN32 #ifndef _WIN32
struct PackageManagerInfo { struct PackageManagerInfo {
String id; String id;
fs::path dbPath; fs::path dbPath;
String countQuery; String countQuery;
}; };
#endif #endif
struct PkgCountCacheData { struct PkgCountCacheData {
u64 count {}; u64 count {};
@ -55,7 +59,7 @@ namespace {
// NOLINTEND(readability-identifier-naming) // NOLINTEND(readability-identifier-naming)
}; };
#ifndef _WIN32 #ifndef _WIN32
fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> { fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
const auto& [pmId, dbPath, countQuery] = pmInfo; const auto& [pmId, dbPath, countQuery] = pmInfo;
@ -117,9 +121,9 @@ namespace {
return count; return count;
} }
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
fn GetNixPackageCount() -> Result<u64, DracError> { fn GetNixPackageCount() -> Result<u64, DracError> {
debug_log("Attempting to get Nix package count."); debug_log("Attempting to get Nix package count.");
@ -140,7 +144,7 @@ namespace {
return GetPackageCountInternalDb(nixInfo); return GetPackageCountInternalDb(nixInfo);
} }
#endif #endif
fn GetCargoPackageCount() -> Result<u64, DracError> { fn GetCargoPackageCount() -> Result<u64, DracError> {
using util::helpers::GetEnv; using util::helpers::GetEnv;
@ -171,12 +175,12 @@ namespace os::shared {
fn GetPackageCount() -> Result<u64, DracError> { fn GetPackageCount() -> Result<u64, DracError> {
u64 count = 0; u64 count = 0;
#ifndef _WIN32 #ifndef _WIN32
if (const Result<u64, DracError> pkgCount = GetNixPackageCount()) if (const Result<u64, DracError> pkgCount = GetNixPackageCount())
count += *pkgCount; count += *pkgCount;
else else
debug_at(pkgCount.error()); debug_at(pkgCount.error());
#endif #endif
if (const Result<u64, DracError> pkgCount = GetCargoPackageCount()) if (const Result<u64, DracError> pkgCount = GetCargoPackageCount())
count += *pkgCount; count += *pkgCount;
@ -186,3 +190,5 @@ namespace os::shared {
return count; return count;
} }
} // namespace os::shared } // namespace os::shared
#endif // !__serenity__

View file

@ -1,11 +1,12 @@
#pragma once #pragma once
#include <chrono> // std::chrono::{days, floor, seconds, system_clock} #include <chrono> // std::chrono::{days, floor, seconds, system_clock}
#include <ctime> // localtime_r/s, strftime, time_t, tm
#include <filesystem> // std::filesystem::path #include <filesystem> // std::filesystem::path
#include <format> // std::format #include <format> // std::format
#include <ftxui/screen/color.hpp> // ftxui::Color #include <ftxui/screen/color.hpp> // ftxui::Color
#include <mutex> // std::{mutex, lock_guard}
#include <iostream> // std::cout #include <iostream> // std::cout
#include <mutex> // std::{mutex, lock_guard}
#include <utility> // std::forward #include <utility> // std::forward
#ifndef NDEBUG #ifndef NDEBUG
@ -169,7 +170,7 @@ namespace util::logging {
const auto nowTp = system_clock::now(); const auto nowTp = system_clock::now();
const std::time_t nowTt = system_clock::to_time_t(nowTp); const std::time_t nowTt = system_clock::to_time_t(nowTp);
std::tm localTm; std::tm localTm {};
String timestamp; String timestamp;
@ -179,13 +180,19 @@ namespace util::logging {
if (localtime_r(&nowTt, &localTm) != nullptr) { if (localtime_r(&nowTt, &localTm) != nullptr) {
#endif #endif
Array<char, 64> timeBuffer {}; Array<char, 64> 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(); timestamp = timeBuffer.data();
else } else {
timestamp = "??:??:?? (strf_err)"; try {
} else timestamp = std::format("{:%X}", nowTp);
} catch (const std::format_error& fmt_err) { timestamp = "??:??:?? (fmt_err)"; }
}
} else {
timestamp = "??:??:?? (conv_err)"; timestamp = "??:??:?? (conv_err)";
}
const String message = std::format(fmt, std::forward<Args>(args)...); const String message = std::format(fmt, std::forward<Args>(args)...);
@ -202,7 +209,6 @@ namespace util::logging {
const String fileLine = const String fileLine =
std::format(LogLevelConst::FILE_LINE_FORMAT, path(loc.file_name()).lexically_normal().string(), loc.line()); 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); const String fullDebugLine = std::format("{}{}", LogLevelConst::DEBUG_LINE_PREFIX, fileLine);
std::cout << '\n' << Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR)); std::cout << '\n' << Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR));
#endif #endif