stuf
This commit is contained in:
parent
013ecdda58
commit
3f4e26ec0f
19 changed files with 3244 additions and 427 deletions
|
@ -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);
|
||||
|
|
|
@ -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) };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"
|
||||
|
|
357
src/main.cpp
357
src/main.cpp
|
@ -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);
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
306
src/ui/ui.cpp
Normal 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
45
src/ui/ui.hpp
Normal 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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue