This commit is contained in:
Mars 2025-05-05 23:19:41 -04:00
parent 013ecdda58
commit 3f4e26ec0f
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
19 changed files with 3244 additions and 427 deletions

View file

@ -13,10 +13,11 @@ AlignConsecutiveShortCaseStatements:
AlignOperands: DontAlign
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortFunctionsOnASingleLine: Empty
AllowShortLoopsOnASingleLine: true
BinPackArguments: false
ColumnLimit: 120
BreakBeforeBraces: Attach
ColumnLimit: 0
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false

2582
include/argparse.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -40,7 +40,8 @@ namespace matchit {
public:
template <typename V>
constexpr explicit MatchHelper(V&& value) : mValue { std::forward<V>(value) } {}
constexpr explicit MatchHelper(V&& value)
: mValue { std::forward<V>(value) } {}
template <typename... PatternPair>
constexpr auto operator()(const PatternPair&... patterns) {
return matchPatterns(std::forward<ValueRefT>(mValue), patterns...);
@ -116,14 +117,18 @@ namespace matchit {
template <typename T>
class EvalTraits<Nullary<T>> {
public:
constexpr static decltype(auto) evalImpl(const Nullary<T>& e) { return e(); }
constexpr static decltype(auto) evalImpl(const Nullary<T>& e) {
return e();
}
};
// Only allowed in nullary
template <typename T>
class EvalTraits<Id<T>> {
public:
constexpr static decltype(auto) evalImpl(const Id<T>& id) { return *const_cast<Id<T>&>(id); }
constexpr static decltype(auto) evalImpl(const Id<T>& id) {
return *const_cast<Id<T>&>(id);
}
};
template <typename Pred>
@ -283,9 +288,11 @@ namespace matchit {
S mEnd;
public:
constexpr Subrange(const I begin, const S end) : mBegin { begin }, mEnd { end } {}
constexpr Subrange(const I begin, const S end)
: mBegin { begin }, mEnd { end } {}
constexpr Subrange(const Subrange& other) : mBegin { other.begin() }, mEnd { other.end() } {}
constexpr Subrange(const Subrange& other)
: mBegin { other.begin() }, mEnd { other.end() } {}
Subrange& operator=(const Subrange& other) {
mBegin = other.begin();
@ -293,9 +300,15 @@ namespace matchit {
return *this;
}
size_t size() const { return static_cast<size_t>(std::distance(mBegin, mEnd)); }
auto begin() const { return mBegin; }
auto end() const { return mEnd; }
size_t size() const {
return static_cast<size_t>(std::distance(mBegin, mEnd));
}
auto begin() const {
return mBegin;
}
auto end() const {
return mEnd;
}
};
template <typename I, typename S>
@ -465,7 +478,8 @@ namespace matchit {
using RetType = std::common_type_t<typename PatternPairs::RetType...>;
};
enum class IdProcess : int32_t { kCANCEL, kCONFIRM };
enum class IdProcess : int32_t { kCANCEL,
kCONFIRM };
template <typename Pattern>
constexpr void processId(const Pattern& pattern, int32_t depth, IdProcess idProcess) {
@ -497,7 +511,9 @@ namespace matchit {
mMemHolder[mSize] = std::forward<T>(t);
++mSize;
}
constexpr auto back() -> ElementT& { return mMemHolder[mSize - 1]; }
constexpr auto back() -> ElementT& {
return mMemHolder[mSize - 1];
}
};
template <>
@ -514,7 +530,7 @@ namespace matchit {
template <typename Value, typename Pattern, typename ConctextT>
constexpr auto matchPattern(Value&& value, const Pattern& pattern, int32_t depth, ConctextT& context) {
const auto result = PatternTraits<Pattern>::matchPatternImpl(std::forward<Value>(value), pattern, depth, context);
const auto result = PatternTraits<Pattern>::matchPatternImpl(std::forward<Value>(value), pattern, depth, context);
const auto process = result ? IdProcess::kCONFIRM : IdProcess::kCANCEL;
processId(pattern, depth, process);
return result;
@ -526,12 +542,15 @@ namespace matchit {
using RetType = std::invoke_result_t<Func>;
using PatternT = Pattern;
constexpr PatternPair(const Pattern& pattern, const Func& func) : mPattern { pattern }, mHandler { func } {}
constexpr PatternPair(const Pattern& pattern, const Func& func)
: mPattern { pattern }, mHandler { func } {}
template <typename Value, typename ContextT>
constexpr bool matchValue(Value&& value, ContextT& context) const {
return matchPattern(std::forward<Value>(value), mPattern, /*depth*/ 0, context);
}
constexpr auto execute() const { return mHandler(); }
constexpr auto execute() const {
return mHandler();
}
private:
const Pattern mPattern;
@ -556,7 +575,8 @@ namespace matchit {
template <typename Pattern>
class PatternHelper {
public:
constexpr explicit PatternHelper(const Pattern& pattern) : mPattern { pattern } {}
constexpr explicit PatternHelper(const Pattern& pattern)
: mPattern { pattern } {}
template <typename Func>
constexpr auto operator=(Func&& func) {
auto f = toNullary(func);
@ -641,8 +661,11 @@ namespace matchit {
template <typename... Patterns>
class Or {
public:
constexpr explicit Or(const Patterns&... patterns) : mPatterns { patterns... } {}
constexpr const auto& patterns() const { return mPatterns; }
constexpr explicit Or(const Patterns&... patterns)
: mPatterns { patterns... } {}
constexpr const auto& patterns() const {
return mPatterns;
}
private:
std::tuple<InternalPatternT<Patterns>...> mPatterns;
@ -713,8 +736,12 @@ namespace matchit {
public:
constexpr App(Unary&& unary, const Pattern& pattern)
: mUnary { std::forward<Unary>(unary) }, mPattern { pattern } {}
constexpr const auto& unary() const { return mUnary; }
constexpr const auto& pattern() const { return mPattern; }
constexpr const auto& unary() const {
return mUnary;
}
constexpr const auto& pattern() const {
return mPattern;
}
private:
const Unary mUnary;
@ -775,8 +802,11 @@ namespace matchit {
template <typename... Patterns>
class And {
public:
constexpr explicit And(const Patterns&... patterns) : mPatterns { patterns... } {}
constexpr const auto& patterns() const { return mPatterns; }
constexpr explicit And(const Patterns&... patterns)
: mPatterns { patterns... } {}
constexpr const auto& patterns() const {
return mPatterns;
}
private:
std::tuple<InternalPatternT<Patterns>...> mPatterns;
@ -835,8 +865,11 @@ namespace matchit {
template <typename Pattern>
class Not {
public:
explicit Not(const Pattern& pattern) : mPattern { pattern } {}
const auto& pattern() const { return mPattern; }
explicit Not(const Pattern& pattern)
: mPattern { pattern } {}
const auto& pattern() const {
return mPattern;
}
private:
InternalPatternT<Pattern> mPattern;
@ -935,10 +968,13 @@ namespace matchit {
ValueVariant<Type> mVariant;
public:
constexpr IdBlockBase() : mDepth {}, mVariant {} {}
constexpr IdBlockBase()
: mDepth {}, mVariant {} {}
constexpr auto& variant() { return mVariant; }
constexpr void reset(const int32_t depth) {
constexpr auto& variant() {
return mVariant;
}
constexpr void reset(const int32_t depth) {
if (mDepth - depth >= 0) {
mVariant = {};
mDepth = depth;
@ -1061,12 +1097,16 @@ namespace matchit {
using BlockVT = std::variant<BlockT, BlockT*>;
BlockVT mBlock = BlockT {};
constexpr decltype(auto) internalValue() const { return block().get(); }
constexpr decltype(auto) internalValue() const {
return block().get();
}
public:
constexpr Id() = default;
constexpr Id(const Id& id) { mBlock = BlockVT { &id.block() }; }
constexpr Id(const Id& id) {
mBlock = BlockVT { &id.block() };
}
// non-const to inform users not to mark Id as const.
template <typename Pattern>
@ -1075,7 +1115,9 @@ namespace matchit {
}
// non-const to inform users not to mark Id as const.
constexpr auto at(const Ooo&) { return OooBinder<Type> { *this }; }
constexpr auto at(const Ooo&) {
return OooBinder<Type> { *this };
}
constexpr BlockT& block() const {
return std::visit(
@ -1094,13 +1136,23 @@ namespace matchit {
IdUtil<Type>::bindValue(block().variant(), std::forward<Value>(v), StorePointer<Type, Value> {});
return true;
}
constexpr void reset(int32_t depth) const { return block().reset(depth); }
constexpr void confirm(int32_t depth) const { return block().confirm(depth); }
constexpr bool hasValue() const { return block().hasValue(); }
constexpr void reset(int32_t depth) const {
return block().reset(depth);
}
constexpr void confirm(int32_t depth) const {
return block().confirm(depth);
}
constexpr bool hasValue() const {
return block().hasValue();
}
// non-const to inform users not to mark Id as const.
constexpr decltype(auto) get() { return block().get(); }
constexpr decltype(auto) get() {
return block().get();
}
// non-const to inform users not to mark Id as const.
constexpr decltype(auto) operator*() { return get(); }
constexpr decltype(auto) operator*() {
return get();
}
};
template <typename Type>
@ -1127,8 +1179,11 @@ namespace matchit {
template <typename... Patterns>
class Ds {
public:
constexpr explicit Ds(const Patterns&... patterns) : mPatterns { patterns... } {}
constexpr const auto& patterns() const { return mPatterns; }
constexpr explicit Ds(const Patterns&... patterns)
: mPatterns { patterns... } {}
constexpr const auto& patterns() const {
return mPatterns;
}
using Type = std::tuple<InternalPatternT<Patterns>...>;
@ -1146,8 +1201,11 @@ namespace matchit {
Id<T> mId;
public:
explicit OooBinder(const Id<T>& id) : mId { id } {}
decltype(auto) binder() const { return mId; }
explicit OooBinder(const Id<T>& id)
: mId { id } {}
decltype(auto) binder() const {
return mId;
}
};
class Ooo {
@ -1404,7 +1462,7 @@ namespace matchit {
using Vs0 = SubTypesT<0, idxOoo, std::tuple<Values...>>;
constexpr static auto isBinder = isOooBinderV<std::tuple_element_t<idxOoo, std::tuple<Patterns...>>>;
// <0, ...int32_t> to workaround compile failure for std::tuple<>.
using ElemT = std::tuple_element_t<0, std::tuple<std::remove_reference_t<Values>..., int32_t>>;
using ElemT = std::tuple_element_t<0, std::tuple<std::remove_reference_t<Values>..., int32_t>>;
constexpr static int64_t diff = static_cast<int64_t>(sizeof...(Values) - sizeof...(Patterns));
constexpr static size_t clippedDiff = static_cast<size_t>(diff > 0 ? diff : 0);
using OooResultTuple =
@ -1522,8 +1580,8 @@ namespace matchit {
}
constexpr auto idxOoo = findOooIdx<typename Ds<Patterns...>::Type>();
constexpr auto isBinder = isOooBinderV<std::tuple_element_t<idxOoo, std::tuple<Patterns...>>>;
auto result = matchPatternRange<0, idxOoo>(std::begin(valueRange), dsPat.patterns(), depth, context);
const auto valLen = valueRange.size();
auto result = matchPatternRange<0, idxOoo>(std::begin(valueRange), dsPat.patterns(), depth, context);
const auto valLen = valueRange.size();
constexpr auto patLen = sizeof...(Patterns);
const auto beginOoo = std::next(std::begin(valueRange), idxOoo);
if constexpr (isBinder) {
@ -1568,9 +1626,14 @@ namespace matchit {
template <typename Pattern, typename Pred>
class PostCheck {
public:
constexpr explicit PostCheck(const Pattern& pattern, const Pred& pred) : mPattern { pattern }, mPred { pred } {}
constexpr bool check() const { return mPred(); }
constexpr const auto& pattern() const { return mPattern; }
constexpr explicit PostCheck(const Pattern& pattern, const Pred& pred)
: mPattern { pattern }, mPred { pred } {}
constexpr bool check() const {
return mPred();
}
constexpr const auto& pattern() const {
return mPattern;
}
private:
const Pattern mPattern;
@ -1742,9 +1805,13 @@ namespace matchit {
return dynamic_cast<T*>(std::addressof(b));
}
constexpr auto operator()(const T& b) const { return std::addressof(b); }
constexpr auto operator()(const T& b) const {
return std::addressof(b);
}
constexpr auto operator()(T& b) const { return std::addressof(b); }
constexpr auto operator()(T& b) const {
return std::addressof(b);
}
};
static_assert(std::is_invocable_v<AsPointer<int>, int>);

View file

@ -88,7 +88,14 @@ add_project_arguments(common_cpp_args, language : 'cpp')
# ------- #
# Files #
# ------- #
base_sources = files('src/core/system_data.cpp', 'src/core/package.cpp', 'src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp')
base_sources = files(
'src/core/system_data.cpp',
'src/core/package.cpp',
'src/config/config.cpp',
'src/config/weather.cpp',
'src/ui/ui.cpp',
'src/main.cpp',
)
platform_sources = {
'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'],

View file

@ -124,7 +124,9 @@ location = "London" # Your city name
const Result<String> envUser = util::helpers::GetEnv("USER");
const Result<String> envLogname = util::helpers::GetEnv("LOGNAME");
defaultName = pwdName ? pwdName : envUser ? *envUser : envLogname ? *envLogname : "User";
defaultName = pwdName ? pwdName : envUser ? *envUser
: envLogname ? *envLogname
: "User";
#endif
std::ofstream file(configPath);

View file

@ -10,12 +10,11 @@
#else
#include <pwd.h> // getpwuid, passwd
#include <unistd.h> // getuid
#include "src/util/helpers.hpp"
#endif
#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"
@ -92,7 +91,9 @@ struct NowPlaying {
* @param tbl The TOML table to parse, containing [now_playing].
* @return A NowPlaying instance with the parsed values, or defaults otherwise.
*/
static fn fromToml(const toml::table& tbl) -> NowPlaying { return { .enabled = tbl["enabled"].value_or(false) }; }
static fn fromToml(const toml::table& tbl) -> NowPlaying {
return { .enabled = tbl["enabled"].value_or(false) };
}
};
/**

View file

@ -10,8 +10,8 @@
#include <glaze/core/context.hpp> // glz::{error_ctx, error_code}
#include <glaze/core/opts.hpp> // glz::opts
#include <glaze/core/reflect.hpp> // glz::format_error
#include <glaze/json/read.hpp> // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read<glz::opts>
#include <variant> // std::{get, holds_alternative}
#include <glaze/json/read.hpp> // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read<glz::opts>
#include <variant> // std::{get, holds_alternative}
#include "src/util/cache.hpp"
#include "src/util/defs.hpp"

View file

@ -1,10 +1,9 @@
#include <format> // std::format
#include <ftxui/dom/elements.hpp> // ftxui::{Element, hbox, vbox, text, separator, filler, etc.}
#include <ftxui/dom/node.hpp> // ftxui::{Render}
#include <ftxui/screen/color.hpp> // ftxui::Color
#include <ftxui/screen/screen.hpp> // ftxui::{Screen, Dimension::Full}
#include <ftxui/screen/string.hpp> // ftxui::string_width
#include <ranges> // std::ranges::{iota, to, transform}
#include "src/ui/ui.hpp"
#ifdef __cpp_lib_print
#include <print> // std::print
@ -13,341 +12,57 @@
#endif
#include "src/config/config.hpp"
#include "src/config/weather.hpp"
#include "src/core/system_data.hpp"
#include "src/util/defs.hpp"
#include "src/util/logging.hpp"
#include "src/util/types.hpp"
namespace ui {
using ftxui::Color;
using util::types::StringView, util::types::i32;
#include "include/argparse.hpp"
// Color themes
struct Theme {
Color::Palette16 icon;
Color::Palette16 label;
Color::Palette16 value;
Color::Palette16 border;
};
using util::types::i32;
static constexpr Theme DEFAULT_THEME = {
.icon = Color::Cyan,
.label = Color::Yellow,
.value = Color::White,
.border = Color::GrayLight,
};
struct Icons {
StringView user;
StringView palette;
StringView calendar;
StringView host;
StringView kernel;
StringView os;
StringView memory;
StringView weather;
StringView music;
StringView disk;
StringView shell;
StringView package;
StringView desktop;
StringView windowManager;
};
[[maybe_unused]] static constexpr Icons NONE = {
.user = "",
.palette = "",
.calendar = "",
.host = "",
.kernel = "",
.os = "",
.memory = "",
.weather = "",
.music = "",
.disk = "",
.shell = "",
.package = "",
.desktop = "",
.windowManager = "",
};
[[maybe_unused]] static constexpr Icons NERD = {
.user = "",
.palette = "",
.calendar = "",
.host = " 󰌢 ",
.kernel = "",
.os = "",
.memory = "",
.weather = "",
.music = "",
.disk = " 󰋊 ",
.shell = "",
.package = " 󰏖 ",
.desktop = " 󰇄 ",
.windowManager = "  ",
};
[[maybe_unused]] static constexpr Icons EMOJI = {
.user = " 👤 ",
.palette = " 🎨 ",
.calendar = " 📅 ",
.host = " 💻 ",
.kernel = " 🫀 ",
.os = " 🤖 ",
.memory = " 🧠 ",
.weather = " 🌈 ",
.music = " 🎵 ",
.disk = " 💾 ",
.shell = " 💲 ",
.package = " 📦 ",
.desktop = " 🖥️ ",
.windowManager = " 🪟 ",
};
static constexpr inline Icons ICON_TYPE = NERD;
} // namespace ui
namespace {
using namespace util::logging;
fn main(const i32 argc, char* argv[]) -> i32 {
using namespace ftxui;
struct RowInfo {
StringView icon;
StringView label;
String value;
};
fn CreateColorCircles() -> Element {
auto colorView =
std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) {
return ftxui::hbox(
{ ftxui::text("") | ftxui::bold | ftxui::color(static_cast<ftxui::Color::Palette256>(colorIndex)),
ftxui::text(" ") }
);
});
return hbox(Elements(std::ranges::begin(colorView), std::ranges::end(colorView)));
}
fn get_visual_width(const String& str) -> usize { return ftxui::string_width(str); }
fn get_visual_width_sv(const StringView& sview) -> usize { return ftxui::string_width(String(sview)); }
fn find_max_label_len(const std::vector<RowInfo>& rows) -> usize {
usize maxWidth = 0;
for (const RowInfo& row : rows) maxWidth = std::max(maxWidth, get_visual_width_sv(row.label));
return maxWidth;
};
fn SystemInfoBox(const Config& config, const os::SystemData& data) -> Element {
const String& name = config.general.name;
const Weather& weather = config.weather;
const auto& [userIcon, paletteIcon, calendarIcon, hostIcon, kernelIcon, osIcon, memoryIcon, weatherIcon, musicIcon, diskIcon, shellIcon, packageIcon, deIcon, wmIcon] =
ui::ICON_TYPE;
std::vector<RowInfo> initialRows; // Date, Weather
std::vector<RowInfo> systemInfoRows; // Host, Kernel, OS, RAM, Disk, Shell, Packages
std::vector<RowInfo> envInfoRows; // DE, WM
if (data.date)
initialRows.push_back({ .icon = calendarIcon, .label = "Date", .value = *data.date });
if (weather.enabled && data.weather) {
const weather::Output& weatherInfo = *data.weather;
String weatherValue = weather.showTownName
? 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({ .icon = weatherIcon, .label = "Weather", .value = std::move(weatherValue) });
} else if (weather.enabled && !data.weather.has_value())
debug_at(data.weather.error());
if (data.host && !data.host->empty())
systemInfoRows.push_back({ .icon = hostIcon, .label = "Host", .value = *data.host });
if (data.osVersion)
systemInfoRows.push_back({ .icon = osIcon, .label = "OS", .value = *data.osVersion });
if (data.kernelVersion)
systemInfoRows.push_back({ .icon = kernelIcon, .label = "Kernel", .value = *data.kernelVersion });
if (data.memInfo)
systemInfoRows.push_back(
{ .icon = memoryIcon, .label = "RAM", .value = std::format("{}", BytesToGiB { *data.memInfo }) }
);
else if (!data.memInfo.has_value())
debug_at(data.memInfo.error());
if (data.diskUsage)
systemInfoRows.push_back(
{
.icon = diskIcon,
.label = "Disk",
.value =
std::format("{}/{}", BytesToGiB { data.diskUsage->usedBytes }, BytesToGiB { data.diskUsage->totalBytes }),
}
);
if (data.shell)
systemInfoRows.push_back({ .icon = shellIcon, .label = "Shell", .value = *data.shell });
if (data.packageCount) {
if (*data.packageCount > 0)
systemInfoRows.push_back(
{ .icon = packageIcon, .label = "Packages", .value = std::format("{}", *data.packageCount) }
);
else
debug_log("Package count is 0, skipping");
}
bool addedDe = false;
if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) {
envInfoRows.push_back({ .icon = deIcon, .label = "DE", .value = *data.desktopEnv });
addedDe = true;
}
if (data.windowMgr) {
if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr))
envInfoRows.push_back({ .icon = wmIcon, .label = "WM", .value = *data.windowMgr });
}
bool nowPlayingActive = false;
String npText;
if (config.nowPlaying.enabled && data.nowPlaying) {
const String title = data.nowPlaying->title.value_or("Unknown Title");
const String artist = data.nowPlaying->artist.value_or("Unknown Artist");
npText = artist + " - " + title;
nowPlayingActive = true;
}
usize maxContentWidth = 0;
const usize greetingWidth = get_visual_width_sv(userIcon) + get_visual_width_sv("Hello ") + get_visual_width(name) +
get_visual_width_sv("! ");
maxContentWidth = std::max(maxContentWidth, greetingWidth);
const usize paletteWidth =
get_visual_width_sv(userIcon) + (16 * (get_visual_width_sv("") + get_visual_width_sv(" ")));
maxContentWidth = std::max(maxContentWidth, paletteWidth);
const usize iconActualWidth = get_visual_width_sv(userIcon);
const usize maxLabelWidthInitial = find_max_label_len(initialRows);
const usize maxLabelWidthSystem = find_max_label_len(systemInfoRows);
const usize maxLabelWidthEnv = find_max_label_len(envInfoRows);
const usize requiredWidthInitialW = iconActualWidth + maxLabelWidthInitial;
const usize requiredWidthSystemW = iconActualWidth + maxLabelWidthSystem;
const usize requiredWidthEnvW = iconActualWidth + maxLabelWidthEnv;
fn calculateRowVisualWidth = [&](const RowInfo& row, const usize requiredLabelVisualWidth) -> usize {
return requiredLabelVisualWidth + get_visual_width(row.value) + get_visual_width_sv(" ");
};
for (const RowInfo& row : initialRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthInitialW));
for (const RowInfo& row : systemInfoRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthSystemW));
for (const RowInfo& row : envInfoRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthEnvW));
const usize targetBoxWidth = maxContentWidth + 2;
usize npFixedWidthLeft = 0;
usize npFixedWidthRight = 0;
if (nowPlayingActive) {
npFixedWidthLeft = get_visual_width_sv(musicIcon) + get_visual_width_sv("Playing") + get_visual_width_sv(" ");
npFixedWidthRight = get_visual_width_sv(" ");
}
i32 paragraphLimit = 1;
if (nowPlayingActive) {
i32 availableForParagraph =
static_cast<i32>(targetBoxWidth) - static_cast<i32>(npFixedWidthLeft) - static_cast<i32>(npFixedWidthRight);
availableForParagraph -= 2;
paragraphLimit = std::max(1, availableForParagraph);
}
fn createStandardRow = [&](const RowInfo& row, const usize sectionRequiredVisualWidth) {
return hbox(
{
hbox(
{
text(String(row.icon)) | color(ui::DEFAULT_THEME.icon),
text(String(row.label)) | color(ui::DEFAULT_THEME.label),
}
) |
size(WIDTH, EQUAL, static_cast<int>(sectionRequiredVisualWidth)),
filler(),
text(row.value) | color(ui::DEFAULT_THEME.value),
text(" "),
}
);
};
Elements content;
content.push_back(text(String(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan));
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
content.push_back(hbox({ text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon), CreateColorCircles() }));
const bool section1Present = !initialRows.empty();
const bool section2Present = !systemInfoRows.empty();
const bool section3Present = !envInfoRows.empty();
if (section1Present)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
for (const RowInfo& row : initialRows) content.push_back(createStandardRow(row, requiredWidthInitialW));
if ((section1Present && (section2Present || section3Present)) || (!section1Present && section2Present))
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
for (const RowInfo& row : systemInfoRows) content.push_back(createStandardRow(row, requiredWidthSystemW));
if (section2Present && section3Present)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
for (const RowInfo& row : envInfoRows) content.push_back(createStandardRow(row, requiredWidthEnvW));
if ((section1Present || section2Present || section3Present) && nowPlayingActive)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
if (nowPlayingActive) {
content.push_back(hbox(
{ text(String(musicIcon)) | color(ui::DEFAULT_THEME.icon),
text("Playing") | color(ui::DEFAULT_THEME.label),
text(" "),
filler(),
paragraphAlignRight(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, paragraphLimit),
text(" ") }
));
}
return vbox(content) | borderRounded | color(Color::White);
}
} // namespace
fn main() -> i32 {
using os::SystemData;
#ifdef _WIN32
winrt::init_apartment();
#endif
argparse::ArgumentParser parser("draconis", "0.1.0");
parser
.add_argument("--log-level")
.help("Set the log level")
.default_value("info")
.choices("trace", "debug", "info", "warn", "error", "fatal");
parser
.add_argument("-V", "--verbose")
.help("Enable verbose logging. Alias for --log-level=debug")
.flag();
try {
parser.parse_args(argc, argv);
} catch (const util::types::Exception& err) {
#ifdef __cpp_lib_print
std::println(stderr, "{}", err.what());
#else
std::cerr << err.what() << '\n';
#endif
std::cerr << parser;
return 1;
}
if (parser["--verbose"] == true || parser["-v"] == true)
info_log("Verbose logging enabled");
const Config& config = Config::getInstance();
const SystemData data = SystemData(config);
Element document = vbox({ hbox({ SystemInfoBox(config, data), filler() }) });
Element document = ui::CreateUI(config, data);
Screen screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);

View file

@ -72,9 +72,13 @@ namespace os {
return Err(DracError(DracErrorCode::NotSupported, "Now playing is not supported on Haiku"));
}
fn GetWindowManager() -> Result<String> { return "app_server"; }
fn GetWindowManager() -> Result<String> {
return "app_server";
}
fn GetDesktopEnvironment() -> Result<String> { return "Haiku Desktop Environment"; }
fn GetDesktopEnvironment() -> Result<String> {
return "Haiku Desktop Environment";
}
fn GetShell() -> Result<String> {
if (const Result<String> shellPath = GetEnv("SHELL")) {

View file

@ -48,15 +48,26 @@ namespace os {
return mem;
}
fn GetNowPlaying() -> Result<MediaInfo> { return GetCurrentPlayingInfo(); }
fn GetNowPlaying() -> Result<MediaInfo> {
return GetCurrentPlayingInfo();
}
fn GetOSVersion() -> Result<String> { return GetMacOSVersion(); }
fn GetOSVersion() -> Result<String> {
return GetMacOSVersion();
}
fn GetDesktopEnvironment() -> Result<String> { return "Aqua"; }
fn GetDesktopEnvironment() -> Result<String> {
return "Aqua";
}
fn GetWindowManager() -> Result<String> {
constexpr Array<StringView, 6> knownWms = {
"yabai", "kwm", "chunkwm", "amethyst", "spectacle", "rectangle",
"yabai",
"kwm",
"chunkwm",
"amethyst",
"spectacle",
"rectangle",
};
Array<i32, 3> request = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };

View file

@ -110,9 +110,13 @@ namespace os {
return Err(DracError(DracErrorCode::NotSupported, "Now playing is not supported on SerenityOS"));
}
fn GetWindowManager() -> Result<String> { return "WindowManager"; }
fn GetWindowManager() -> Result<String> {
return "WindowManager";
}
fn GetDesktopEnvironment() -> Result<String> { return "SerenityOS Desktop"; }
fn GetDesktopEnvironment() -> Result<String> {
return "SerenityOS Desktop";
}
fn GetShell() -> Result<String> {
uid_t userId = getuid();
@ -166,7 +170,9 @@ namespace os {
} // namespace os
namespace package {
fn GetSerenityCount() -> Result<u64> { return CountUniquePackages("/usr/Ports/installed.db"); }
fn GetSerenityCount() -> Result<u64> {
return CountUniquePackages("/usr/Ports/installed.db");
}
} // namespace package
#endif // __serenity__

306
src/ui/ui.cpp Normal file
View file

@ -0,0 +1,306 @@
#include "ui.hpp"
#include "src/util/types.hpp"
namespace ui {
using namespace ftxui;
using namespace util::types;
constexpr Theme DEFAULT_THEME = {
.icon = Color::Cyan,
.label = Color::Yellow,
.value = Color::White,
.border = Color::GrayLight,
};
[[maybe_unused]] static constexpr Icons NONE = {
.user = "",
.palette = "",
.calendar = "",
.host = "",
.kernel = "",
.os = "",
.memory = "",
.weather = "",
.music = "",
.disk = "",
.shell = "",
.package = "",
.desktop = "",
.windowManager = "",
};
[[maybe_unused]] static constexpr Icons NERD = {
.user = "",
.palette = "",
.calendar = "",
.host = " 󰌢 ",
.kernel = "",
.os = "",
.memory = "",
.weather = "",
.music = "",
.disk = " 󰋊 ",
.shell = "",
.package = " 󰏖 ",
.desktop = " 󰇄 ",
.windowManager = "  ",
};
[[maybe_unused]] static constexpr Icons EMOJI = {
.user = " 👤 ",
.palette = " 🎨 ",
.calendar = " 📅 ",
.host = " 💻 ",
.kernel = " 🫀 ",
.os = " 🤖 ",
.memory = " 🧠 ",
.weather = " 🌈 ",
.music = " 🎵 ",
.disk = " 💾 ",
.shell = " 💲 ",
.package = " 📦 ",
.desktop = " 🖥️ ",
.windowManager = " 🪟 ",
};
constexpr inline Icons ICON_TYPE = NERD;
struct RowInfo {
StringView icon;
StringView label;
String value;
};
namespace {
fn CreateColorCircles() -> Element {
auto colorView =
std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) {
return ftxui::hbox(
{ ftxui::text("") | ftxui::bold | ftxui::color(static_cast<ftxui::Color::Palette256>(colorIndex)),
ftxui::text(" ") }
);
});
return hbox(Elements(std::ranges::begin(colorView), std::ranges::end(colorView)));
}
fn get_visual_width(const String& str) -> usize {
return ftxui::string_width(str);
}
fn get_visual_width_sv(const StringView& sview) -> usize {
return ftxui::string_width(String(sview));
}
fn find_max_label_len(const std::vector<RowInfo>& rows) -> usize {
usize maxWidth = 0;
for (const RowInfo& row : rows) maxWidth = std::max(maxWidth, get_visual_width_sv(row.label));
return maxWidth;
};
fn CreateInfoBox(const Config& config, const os::SystemData& data) -> Element {
const String& name = config.general.name;
const Weather& weather = config.weather;
const auto& [userIcon, paletteIcon, calendarIcon, hostIcon, kernelIcon, osIcon, memoryIcon, weatherIcon, musicIcon, diskIcon, shellIcon, packageIcon, deIcon, wmIcon] =
ui::ICON_TYPE;
std::vector<RowInfo> initialRows; // Date, Weather
std::vector<RowInfo> systemInfoRows; // Host, Kernel, OS, RAM, Disk, Shell, Packages
std::vector<RowInfo> envInfoRows; // DE, WM
if (data.date)
initialRows.push_back({ .icon = calendarIcon, .label = "Date", .value = *data.date });
if (weather.enabled && data.weather) {
const weather::Output& weatherInfo = *data.weather;
String weatherValue = weather.showTownName
? 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({ .icon = weatherIcon, .label = "Weather", .value = std::move(weatherValue) });
} else if (weather.enabled && !data.weather.has_value())
debug_at(data.weather.error());
if (data.host && !data.host->empty())
systemInfoRows.push_back({ .icon = hostIcon, .label = "Host", .value = *data.host });
if (data.osVersion)
systemInfoRows.push_back({ .icon = osIcon, .label = "OS", .value = *data.osVersion });
if (data.kernelVersion)
systemInfoRows.push_back({ .icon = kernelIcon, .label = "Kernel", .value = *data.kernelVersion });
if (data.memInfo)
systemInfoRows.push_back(
{ .icon = memoryIcon, .label = "RAM", .value = std::format("{}", BytesToGiB { *data.memInfo }) }
);
else if (!data.memInfo.has_value())
debug_at(data.memInfo.error());
if (data.diskUsage)
systemInfoRows.push_back(
{
.icon = diskIcon,
.label = "Disk",
.value =
std::format("{}/{}", BytesToGiB { data.diskUsage->usedBytes }, BytesToGiB { data.diskUsage->totalBytes }),
}
);
if (data.shell)
systemInfoRows.push_back({ .icon = shellIcon, .label = "Shell", .value = *data.shell });
if (data.packageCount) {
if (*data.packageCount > 0)
systemInfoRows.push_back(
{ .icon = packageIcon, .label = "Packages", .value = std::format("{}", *data.packageCount) }
);
else
debug_log("Package count is 0, skipping");
}
bool addedDe = false;
if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) {
envInfoRows.push_back({ .icon = deIcon, .label = "DE", .value = *data.desktopEnv });
addedDe = true;
}
if (data.windowMgr)
if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr))
envInfoRows.push_back({ .icon = wmIcon, .label = "WM", .value = *data.windowMgr });
bool nowPlayingActive = false;
String npText;
if (config.nowPlaying.enabled && data.nowPlaying) {
const String title = data.nowPlaying->title.value_or("Unknown Title");
const String artist = data.nowPlaying->artist.value_or("Unknown Artist");
npText = artist + " - " + title;
nowPlayingActive = true;
}
usize maxContentWidth = 0;
const usize greetingWidth = get_visual_width_sv(userIcon) + get_visual_width_sv("Hello ") + get_visual_width(name) +
get_visual_width_sv("! ");
maxContentWidth = std::max(maxContentWidth, greetingWidth);
const usize paletteWidth =
get_visual_width_sv(userIcon) + (16 * (get_visual_width_sv("") + get_visual_width_sv(" ")));
maxContentWidth = std::max(maxContentWidth, paletteWidth);
const usize iconActualWidth = get_visual_width_sv(userIcon);
const usize maxLabelWidthInitial = find_max_label_len(initialRows);
const usize maxLabelWidthSystem = find_max_label_len(systemInfoRows);
const usize maxLabelWidthEnv = find_max_label_len(envInfoRows);
const usize requiredWidthInitialW = iconActualWidth + maxLabelWidthInitial;
const usize requiredWidthSystemW = iconActualWidth + maxLabelWidthSystem;
const usize requiredWidthEnvW = iconActualWidth + maxLabelWidthEnv;
fn calculateRowVisualWidth = [&](const RowInfo& row, const usize requiredLabelVisualWidth) -> usize {
return requiredLabelVisualWidth + get_visual_width(row.value) + get_visual_width_sv(" ");
};
for (const RowInfo& row : initialRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthInitialW));
for (const RowInfo& row : systemInfoRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthSystemW));
for (const RowInfo& row : envInfoRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthEnvW));
const usize targetBoxWidth = maxContentWidth + 2;
usize npFixedWidthLeft = 0;
usize npFixedWidthRight = 0;
if (nowPlayingActive) {
npFixedWidthLeft = get_visual_width_sv(musicIcon) + get_visual_width_sv("Playing") + get_visual_width_sv(" ");
npFixedWidthRight = get_visual_width_sv(" ");
}
i32 paragraphLimit = 1;
if (nowPlayingActive) {
i32 availableForParagraph =
static_cast<i32>(targetBoxWidth) - static_cast<i32>(npFixedWidthLeft) - static_cast<i32>(npFixedWidthRight);
availableForParagraph -= 2;
paragraphLimit = std::max(1, availableForParagraph);
}
fn createStandardRow = [&](const RowInfo& row, const usize sectionRequiredVisualWidth) {
return hbox(
{
hbox(
{
text(String(row.icon)) | color(ui::DEFAULT_THEME.icon),
text(String(row.label)) | color(ui::DEFAULT_THEME.label),
}
) |
size(WIDTH, EQUAL, static_cast<int>(sectionRequiredVisualWidth)),
filler(),
text(row.value) | color(ui::DEFAULT_THEME.value),
text(" "),
}
);
};
Elements content;
content.push_back(text(String(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan));
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
content.push_back(hbox({ text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon), CreateColorCircles() }));
const bool section1Present = !initialRows.empty();
const bool section2Present = !systemInfoRows.empty();
const bool section3Present = !envInfoRows.empty();
if (section1Present)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
for (const RowInfo& row : initialRows) content.push_back(createStandardRow(row, requiredWidthInitialW));
if ((section1Present && (section2Present || section3Present)) || (!section1Present && section2Present))
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
for (const RowInfo& row : systemInfoRows) content.push_back(createStandardRow(row, requiredWidthSystemW));
if (section2Present && section3Present)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
for (const RowInfo& row : envInfoRows) content.push_back(createStandardRow(row, requiredWidthEnvW));
if ((section1Present || section2Present || section3Present) && nowPlayingActive)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
if (nowPlayingActive) {
content.push_back(hbox(
{
text(String(musicIcon)) | color(ui::DEFAULT_THEME.icon),
text("Playing") | color(ui::DEFAULT_THEME.label),
text(" "),
filler(),
paragraphAlignRight(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, paragraphLimit),
text(" "),
}
));
}
return vbox(content) | borderRounded | color(Color::White);
}
} // namespace
fn CreateUI(const Config& config, const os::SystemData& data) -> Element {
Element infoBox = CreateInfoBox(config, data);
return hbox({ infoBox, filler() });
}
} // namespace ui

45
src/ui/ui.hpp Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#include <ftxui/dom/elements.hpp> // ftxui::Element
#include <ftxui/screen/color.hpp> // ftxui::Color
#include "src/config/config.hpp"
#include "src/core/system_data.hpp"
#include "src/util/types.hpp"
namespace ui {
struct Theme {
ftxui::Color::Palette16 icon;
ftxui::Color::Palette16 label;
ftxui::Color::Palette16 value;
ftxui::Color::Palette16 border;
};
extern const Theme DEFAULT_THEME;
struct Icons {
util::types::StringView user;
util::types::StringView palette;
util::types::StringView calendar;
util::types::StringView host;
util::types::StringView kernel;
util::types::StringView os;
util::types::StringView memory;
util::types::StringView weather;
util::types::StringView music;
util::types::StringView disk;
util::types::StringView shell;
util::types::StringView package;
util::types::StringView desktop;
util::types::StringView windowManager;
};
extern const Icons ICON_TYPE;
/**
* @brief Creates the main UI element based on system data and configuration.
* @param config The application configuration.
* @param data The collected system data. @return The root ftxui::Element for rendering.
*/
fn CreateUI(const Config& config, const os::SystemData& data) -> ftxui::Element;
} // namespace ui

View file

@ -12,7 +12,7 @@
#include "src/util/types.hpp"
#include "include/matchit.h"
#include "include/matchit.hpp"
namespace util {
namespace error {
@ -72,12 +72,13 @@ namespace util {
is | or_(no_such_file_or_directory, not_a_directory, is_a_directory, file_exists) = NotFound,
is | permission_denied = PermissionDenied,
is | timed_out = Timeout,
is | _ = errc.category() == std::generic_category() ? InternalError : PlatformSpecific
is | _ = errc.category() == std::generic_category() ? InternalError : PlatformSpecific
);
}
#ifdef _WIN32
explicit DracError(const winrt::hresult_error& e) : message(winrt::to_string(e.message())) {
explicit DracError(const winrt::hresult_error& e)
: message(winrt::to_string(e.message())) {
using namespace matchit;
using enum DracErrorCode;

View file

@ -74,7 +74,10 @@ namespace util::logging {
* @enum LogLevel
* @brief Represents different log levels.
*/
enum class LogLevel : u8 { Debug, Info, Warn, Error };
enum class LogLevel : u8 { Debug,
Info,
Warn,
Error };
/**
* @brief Directly applies ANSI color codes to text
@ -227,7 +230,8 @@ namespace util::logging {
#ifdef __cpp_lib_print
std::print("\n{}", Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR)));
#else
std::cout << '\n' << Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR));
std::cout << '\n'
<< Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR));
#endif
#endif

View file

@ -131,6 +131,7 @@ namespace util::types {
MediaInfo() = default;
MediaInfo(Option<String> title, Option<String> artist) : title(std::move(title)), artist(std::move(artist)) {}
MediaInfo(Option<String> title, Option<String> artist)
: title(std::move(title)), artist(std::move(artist)) {}
};
} // namespace util::types

View file

@ -27,7 +27,10 @@ namespace dbus {
bool m_isInitialized = false;
public:
Error() : m_isInitialized(true) { dbus_error_init(&m_err); }
Error()
: m_isInitialized(true) {
dbus_error_init(&m_err);
}
~Error() {
if (m_isInitialized)
@ -37,7 +40,8 @@ namespace dbus {
Error(const Error&) = delete;
fn operator=(const Error&)->Error& = delete;
Error(Error&& other) noexcept : m_err(other.m_err), m_isInitialized(other.m_isInitialized) {
Error(Error&& other) noexcept
: m_err(other.m_err), m_isInitialized(other.m_isInitialized) {
other.m_isInitialized = false;
dbus_error_init(&other.m_err);
}
@ -60,30 +64,40 @@ namespace dbus {
* @brief Checks if the D-Bus error is set.
* @return True if an error is set, false otherwise.
*/
[[nodiscard]] fn isSet() const -> bool { return m_isInitialized && dbus_error_is_set(&m_err); }
[[nodiscard]] fn isSet() const -> bool {
return m_isInitialized && dbus_error_is_set(&m_err);
}
/**
* @brief Gets the error message.
* @return The error message string, or "" if not set or not initialized.
*/
[[nodiscard]] fn message() const -> const char* { return isSet() ? m_err.message : ""; }
[[nodiscard]] fn message() const -> const char* {
return isSet() ? m_err.message : "";
}
/**
* @brief Gets the error name.
* @return The error name string (e.g., "org.freedesktop.DBus.Error.Failed"), or "" if not set or not initialized.
*/
[[nodiscard]] fn name() const -> const char* { return isSet() ? m_err.name : ""; }
[[nodiscard]] fn name() const -> const char* {
return isSet() ? m_err.name : "";
}
/**
* @brief Gets a pointer to the underlying DBusError. Use with caution.
* @return Pointer to the DBusError struct.
*/
[[nodiscard]] fn get() -> DBusError* { return &m_err; }
[[nodiscard]] fn get() -> DBusError* {
return &m_err;
}
/**
* @brief Gets a const pointer to the underlying DBusError.
* @return Const pointer to the DBusError struct.
*/
[[nodiscard]] fn get() const -> const DBusError* { return &m_err; }
[[nodiscard]] fn get() const -> const DBusError* {
return &m_err;
}
/**
* @brief Converts the D-Bus error to a DraconisError.
@ -107,7 +121,8 @@ namespace dbus {
DBusMessageIter m_iter {};
bool m_isValid = false;
explicit MessageIter(const DBusMessageIter& iter, const bool isValid) : m_iter(iter), m_isValid(isValid) {}
explicit MessageIter(const DBusMessageIter& iter, const bool isValid)
: m_iter(iter), m_isValid(isValid) {}
friend class Message;
@ -131,7 +146,9 @@ namespace dbus {
/**
* @brief Checks if the iterator is validly initialized.
*/
[[nodiscard]] fn isValid() const -> bool { return m_isValid; }
[[nodiscard]] fn isValid() const -> bool {
return m_isValid;
}
/**
* @brief Gets the D-Bus type code of the current argument.
@ -154,7 +171,9 @@ namespace dbus {
* @brief Advances the iterator to the next argument.
* @return True if successful (moved to a next element), false if at the end or iterator is invalid.
*/
fn next() -> bool { return m_isValid && dbus_message_iter_next(&m_iter); }
fn next() -> bool {
return m_isValid && dbus_message_iter_next(&m_iter);
}
/**
* @brief Recurses into a container-type argument (e.g., array, struct, variant).
@ -197,7 +216,8 @@ namespace dbus {
DBusMessage* m_msg = nullptr;
public:
explicit Message(DBusMessage* msg = nullptr) : m_msg(msg) {}
explicit Message(DBusMessage* msg = nullptr)
: m_msg(msg) {}
~Message() {
if (m_msg)
@ -207,7 +227,8 @@ namespace dbus {
Message(const Message&) = delete;
fn operator=(const Message&)->Message& = delete;
Message(Message&& other) noexcept : m_msg(std::exchange(other.m_msg, nullptr)) {}
Message(Message&& other) noexcept
: m_msg(std::exchange(other.m_msg, nullptr)) {}
fn operator=(Message&& other) noexcept -> Message& {
if (this != &other) {
@ -222,7 +243,9 @@ namespace dbus {
* @brief Gets the underlying DBusMessage pointer. Use with caution.
* @return The raw DBusMessage pointer, or nullptr if not holding a message.
*/
[[nodiscard]] fn get() const -> DBusMessage* { return m_msg; }
[[nodiscard]] fn get() const -> DBusMessage* {
return m_msg;
}
/**
* @brief Initializes a message iterator for reading arguments from this message.
@ -296,7 +319,8 @@ namespace dbus {
DBusConnection* m_conn = nullptr;
public:
explicit Connection(DBusConnection* conn = nullptr) : m_conn(conn) {}
explicit Connection(DBusConnection* conn = nullptr)
: m_conn(conn) {}
~Connection() {
if (m_conn)
@ -306,7 +330,8 @@ namespace dbus {
Connection(const Connection&) = delete;
fn operator=(const Connection&)->Connection& = delete;
Connection(Connection&& other) noexcept : m_conn(std::exchange(other.m_conn, nullptr)) {}
Connection(Connection&& other) noexcept
: m_conn(std::exchange(other.m_conn, nullptr)) {}
fn operator=(Connection&& other) noexcept -> Connection& {
if (this != &other) {
@ -322,7 +347,9 @@ namespace dbus {
* @brief Gets the underlying DBusConnection pointer. Use with caution.
* @return The raw DBusConnection pointer, or nullptr if not holding a connection.
*/
[[nodiscard]] fn get() const -> DBusConnection* { return m_conn; }
[[nodiscard]] fn get() const -> DBusConnection* {
return m_conn;
}
/**
* @brief Sends a message and waits for a reply, blocking execution.

View file

@ -16,9 +16,15 @@ namespace wl {
using display = wl_display;
// NOLINTBEGIN(readability-identifier-naming)
inline fn connect(const char* name) -> display* { return wl_display_connect(name); }
inline fn disconnect(display* display) -> void { wl_display_disconnect(display); }
inline fn get_fd(display* display) -> int { return wl_display_get_fd(display); }
inline fn connect(const char* name) -> display* {
return wl_display_connect(name);
}
inline fn disconnect(display* display) -> void {
wl_display_disconnect(display);
}
inline fn get_fd(display* display) -> int {
return wl_display_get_fd(display);
}
// NOLINTEND(readability-identifier-naming)
/**
@ -77,7 +83,8 @@ namespace wl {
fn operator=(const DisplayGuard&)->DisplayGuard& = delete;
// Movable
DisplayGuard(DisplayGuard&& other) noexcept : m_display(std::exchange(other.m_display, nullptr)) {}
DisplayGuard(DisplayGuard&& other) noexcept
: m_display(std::exchange(other.m_display, nullptr)) {}
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard& {
if (this != &other) {
if (m_display)
@ -89,10 +96,16 @@ namespace wl {
return *this;
}
[[nodiscard]] explicit operator bool() const { return m_display != nullptr; }
[[nodiscard]] explicit operator bool() const {
return m_display != nullptr;
}
[[nodiscard]] fn get() const -> display* { return m_display; }
[[nodiscard]] fn fd() const -> util::types::i32 { return get_fd(m_display); }
[[nodiscard]] fn get() const -> display* {
return m_display;
}
[[nodiscard]] fn fd() const -> util::types::i32 {
return get_fd(m_display);
}
};
} // namespace wl

View file

@ -53,8 +53,12 @@ namespace xcb {
inline fn connect(const char* displayname, int* screenp) -> connection_t* {
return xcb_connect(displayname, screenp);
}
inline fn disconnect(connection_t* conn) -> void { xcb_disconnect(conn); }
inline fn connection_has_error(connection_t* conn) -> int { return xcb_connection_has_error(conn); }
inline fn disconnect(connection_t* conn) -> void {
xcb_disconnect(conn);
}
inline fn connection_has_error(connection_t* conn) -> int {
return xcb_connection_has_error(conn);
}
inline fn intern_atom(connection_t* conn, const uint8_t only_if_exists, const uint16_t name_len, const char* name)
-> intern_atom_cookie_t {
return xcb_intern_atom(conn, only_if_exists, name_len, name);
@ -81,7 +85,9 @@ namespace xcb {
inline fn get_property_value_length(const get_property_reply_t* reply) -> int {
return xcb_get_property_value_length(reply);
}
inline fn get_property_value(const get_property_reply_t* reply) -> void* { return xcb_get_property_value(reply); }
inline fn get_property_value(const get_property_reply_t* reply) -> void* {
return xcb_get_property_value(reply);
}
// NOLINTEND(readability-identifier-naming)
/**
@ -96,7 +102,8 @@ namespace xcb {
* Opens an XCB connection
* @param name Display name (nullptr for default)
*/
explicit DisplayGuard(const util::types::CStr name = nullptr) : m_connection(connect(name, nullptr)) {}
explicit DisplayGuard(const util::types::CStr name = nullptr)
: m_connection(connect(name, nullptr)) {}
~DisplayGuard() {
if (m_connection)
disconnect(m_connection);
@ -107,7 +114,8 @@ namespace xcb {
fn operator=(const DisplayGuard&)->DisplayGuard& = delete;
// Movable
DisplayGuard(DisplayGuard&& other) noexcept : m_connection(std::exchange(other.m_connection, nullptr)) {}
DisplayGuard(DisplayGuard&& other) noexcept
: m_connection(std::exchange(other.m_connection, nullptr)) {}
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard& {
if (this != &other) {
if (m_connection)
@ -118,11 +126,17 @@ namespace xcb {
return *this;
}
[[nodiscard]] explicit operator bool() const { return m_connection && !connection_has_error(m_connection); }
[[nodiscard]] explicit operator bool() const {
return m_connection && !connection_has_error(m_connection);
}
[[nodiscard]] fn get() const -> connection_t* { return m_connection; }
[[nodiscard]] fn get() const -> connection_t* {
return m_connection;
}
[[nodiscard]] fn setup() const -> const setup_t* { return m_connection ? xcb_get_setup(m_connection) : nullptr; }
[[nodiscard]] fn setup() const -> const setup_t* {
return m_connection ? xcb_get_setup(m_connection) : nullptr;
}
[[nodiscard]] fn rootScreen() const -> screen_t* {
const setup_t* setup = this->setup();
@ -140,7 +154,8 @@ namespace xcb {
public:
ReplyGuard() = default;
explicit ReplyGuard(T* reply) : m_reply(reply) {}
explicit ReplyGuard(T* reply)
: m_reply(reply) {}
~ReplyGuard() {
if (m_reply)
@ -152,7 +167,8 @@ namespace xcb {
fn operator=(const ReplyGuard&)->ReplyGuard& = delete;
// Movable
ReplyGuard(ReplyGuard&& other) noexcept : m_reply(std::exchange(other.m_reply, nullptr)) {}
ReplyGuard(ReplyGuard&& other) noexcept
: m_reply(std::exchange(other.m_reply, nullptr)) {}
fn operator=(ReplyGuard&& other) noexcept -> ReplyGuard& {
if (this != &other) {
if (m_reply)
@ -163,11 +179,19 @@ namespace xcb {
return *this;
}
[[nodiscard]] explicit operator bool() const { return m_reply != nullptr; }
[[nodiscard]] explicit operator bool() const {
return m_reply != nullptr;
}
[[nodiscard]] fn get() const -> T* { return m_reply; }
[[nodiscard]] fn operator->() const->T* { return m_reply; }
[[nodiscard]] fn operator*() const->T& { return *m_reply; }
[[nodiscard]] fn get() const -> T* {
return m_reply;
}
[[nodiscard]] fn operator->() const->T* {
return m_reply;
}
[[nodiscard]] fn operator*() const->T& {
return *m_reply;
}
};
} // namespace xcb