more stuffs
This commit is contained in:
parent
801a8d1754
commit
55819ebfe0
10 changed files with 410 additions and 237 deletions
|
@ -1,7 +1,6 @@
|
||||||
# This file was generated by nvfetcher, please do not modify it manually.
|
# This file was generated by nvfetcher, please do not modify it manually.
|
||||||
|
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
|
||||||
{
|
{
|
||||||
fetchFromGitHub,
|
|
||||||
}: {
|
|
||||||
dbus-cxx = {
|
dbus-cxx = {
|
||||||
pname = "dbus-cxx";
|
pname = "dbus-cxx";
|
||||||
version = "2.5.2";
|
version = "2.5.2";
|
||||||
|
|
6
flake.lock
generated
6
flake.lock
generated
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744868846,
|
"lastModified": 1745377448,
|
||||||
"narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=",
|
"narHash": "sha256-jhZDfXVKdD7TSEGgzFJQvEEZ2K65UMiqW5YJ2aIqxMA=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c",
|
"rev": "507b63021ada5fee621b6ca371c4fca9ca46f52c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
dbus-cxx
|
dbus-cxx
|
||||||
libsigcxx30
|
libsigcxx30
|
||||||
sqlitecpp
|
sqlitecpp
|
||||||
xorg.libX11
|
xorg.libxcb
|
||||||
wayland
|
wayland
|
||||||
]));
|
]));
|
||||||
in
|
in
|
||||||
|
|
|
@ -130,7 +130,6 @@ elif host_system == 'windows'
|
||||||
elif host_system == 'linux'
|
elif host_system == 'linux'
|
||||||
platform_deps += [
|
platform_deps += [
|
||||||
dependency('SQLiteCpp'),
|
dependency('SQLiteCpp'),
|
||||||
dependency('x11'),
|
|
||||||
dependency('xcb'),
|
dependency('xcb'),
|
||||||
dependency('xau'),
|
dependency('xau'),
|
||||||
dependency('xdmcp'),
|
dependency('xdmcp'),
|
||||||
|
@ -164,9 +163,7 @@ glaze_dep = dependency('glaze', include_type : 'system', required : false)
|
||||||
|
|
||||||
if not glaze_dep.found()
|
if not glaze_dep.found()
|
||||||
cmake = import('cmake')
|
cmake = import('cmake')
|
||||||
|
|
||||||
glaze_proj = cmake.subproject('glaze')
|
glaze_proj = cmake.subproject('glaze')
|
||||||
|
|
||||||
glaze_dep = glaze_proj.dependency('glaze_glaze', include_type : 'system')
|
glaze_dep = glaze_proj.dependency('glaze_glaze', include_type : 'system')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -3,34 +3,80 @@
|
||||||
#include "src/config/config.h"
|
#include "src/config/config.h"
|
||||||
#include "src/util/types.h"
|
#include "src/util/types.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct BytesToGiB
|
||||||
|
* @brief Helper struct to format a byte value to GiB (Gibibytes).
|
||||||
|
*
|
||||||
|
* Encapsulates a byte value and provides a custom formatter
|
||||||
|
* to convert it to GiB for display purposes.
|
||||||
|
*/
|
||||||
struct BytesToGiB {
|
struct BytesToGiB {
|
||||||
u64 value;
|
u64 value; ///< The byte value to be converted.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Conversion factor from bytes to GiB
|
||||||
constexpr u64 GIB = 1'073'741'824;
|
constexpr u64 GIB = 1'073'741'824;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Custom formatter for BytesToGiB.
|
||||||
|
*
|
||||||
|
* Allows formatting BytesToGiB values using std::format.
|
||||||
|
* Outputs the value in GiB with two decimal places.
|
||||||
|
*
|
||||||
|
* @code{.cpp}
|
||||||
|
* #include <format>
|
||||||
|
* #include "system_data.h" // Assuming BytesToGiB is defined here
|
||||||
|
*
|
||||||
|
* i32 main() {
|
||||||
|
* BytesToGiB data_size{2'147'483'648}; // 2 GiB
|
||||||
|
* String formatted = std::format("Size: {}", data_size);
|
||||||
|
* std::println("{}", formatted); // formatted will be "Size: 2.00GiB"
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
template <>
|
template <>
|
||||||
struct std::formatter<BytesToGiB> : std::formatter<double> {
|
struct std::formatter<BytesToGiB> : std::formatter<double> {
|
||||||
|
/**
|
||||||
|
* @brief Formats the BytesToGiB value.
|
||||||
|
* @param BTG The BytesToGiB instance to format.
|
||||||
|
* @param ctx The formatting context.
|
||||||
|
* @return An iterator to the end of the formatted output.
|
||||||
|
*/
|
||||||
fn format(const BytesToGiB& BTG, auto& ctx) const {
|
fn format(const BytesToGiB& BTG, auto& ctx) const {
|
||||||
return std::format_to(ctx.out(), "{:.2f}GiB", static_cast<f64>(BTG.value) / GIB);
|
return std::format_to(ctx.out(), "{:.2f}GiB", static_cast<f64>(BTG.value) / GIB);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structure to hold the collected system data
|
/**
|
||||||
|
* @struct SystemData
|
||||||
|
* @brief Holds various pieces of system information collected from the OS.
|
||||||
|
*
|
||||||
|
* This structure aggregates information about the system,
|
||||||
|
* in order to display it at all at once during runtime.
|
||||||
|
*/
|
||||||
struct SystemData {
|
struct SystemData {
|
||||||
String date;
|
using NowPlayingResult = Option<Result<String, NowPlayingError>>;
|
||||||
String host;
|
|
||||||
String kernel_version;
|
|
||||||
Result<String, String> os_version;
|
|
||||||
Result<u64, String> mem_info;
|
|
||||||
Option<String> desktop_environment;
|
|
||||||
String window_manager;
|
|
||||||
Option<Result<String, NowPlayingError>> now_playing;
|
|
||||||
Option<WeatherOutput> weather_info;
|
|
||||||
u64 disk_used;
|
|
||||||
u64 disk_total;
|
|
||||||
String shell;
|
|
||||||
|
|
||||||
// Static function to fetch the data
|
// clang-format off
|
||||||
|
String date; ///< Current date (e.g., "April 24th").
|
||||||
|
String host; ///< Host or product family name (e.g., "MacBook Pro").
|
||||||
|
String kernel_version; ///< OS kernel version (e.g., "5.15.0-generic").
|
||||||
|
Result<String, String> os_version; ///< OS pretty name (e.g., "Ubuntu 22.04 LTS") or an error message.
|
||||||
|
Result<u64, String> mem_info; ///< Total physical RAM in bytes or an error message.
|
||||||
|
Option<String> desktop_environment; ///< Detected desktop environment (e.g., "GNOME", "KDE", "Fluent (Windows 11)"). Might be None.
|
||||||
|
String window_manager; ///< Detected window manager (e.g., "Mutter", "KWin", "DWM").
|
||||||
|
NowPlayingResult now_playing; ///< Currently playing media ("Artist - Title") or an error/None if disabled/unavailable.
|
||||||
|
Option<WeatherOutput> weather_info; ///< Weather information or None if disabled/unavailable.
|
||||||
|
u64 disk_used; ///< Used disk space in bytes for the root filesystem.
|
||||||
|
u64 disk_total; ///< Total disk space in bytes for the root filesystem.
|
||||||
|
String shell; ///< Name of the current user shell (e.g., "Bash", "Zsh", "PowerShell").
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetches all system data asynchronously.
|
||||||
|
* @param config The application configuration.
|
||||||
|
* @return A populated SystemData object.
|
||||||
|
*/
|
||||||
static fn fetchSystemData(const Config& config) -> SystemData;
|
static fn fetchSystemData(const Config& config) -> SystemData;
|
||||||
};
|
};
|
||||||
|
|
71
src/main.cpp
71
src/main.cpp
|
@ -33,19 +33,19 @@ namespace ui {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Icons {
|
struct Icons {
|
||||||
[[maybe_unused]] StringView user;
|
StringView user;
|
||||||
[[maybe_unused]] StringView palette;
|
StringView palette;
|
||||||
[[maybe_unused]] StringView calendar;
|
StringView calendar;
|
||||||
[[maybe_unused]] StringView host;
|
StringView host;
|
||||||
[[maybe_unused]] StringView kernel;
|
StringView kernel;
|
||||||
[[maybe_unused]] StringView os;
|
StringView os;
|
||||||
[[maybe_unused]] StringView memory;
|
StringView memory;
|
||||||
[[maybe_unused]] StringView weather;
|
StringView weather;
|
||||||
[[maybe_unused]] StringView music;
|
StringView music;
|
||||||
[[maybe_unused]] StringView disk;
|
StringView disk;
|
||||||
[[maybe_unused]] StringView shell;
|
StringView shell;
|
||||||
[[maybe_unused]] StringView desktop;
|
StringView desktop;
|
||||||
[[maybe_unused]] StringView window_manager;
|
StringView window_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Icons EMPTY_ICONS = {
|
static constexpr Icons EMPTY_ICONS = {
|
||||||
|
@ -105,21 +105,25 @@ namespace {
|
||||||
|
|
||||||
content.push_back(text(String(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan));
|
content.push_back(text(String(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan));
|
||||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||||
content.push_back(hbox({
|
content.push_back(hbox(
|
||||||
|
{
|
||||||
text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon),
|
text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon),
|
||||||
CreateColorCircles(),
|
CreateColorCircles(),
|
||||||
}));
|
}
|
||||||
|
));
|
||||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||||
|
|
||||||
// Helper function for aligned rows
|
// Helper function for aligned rows
|
||||||
fn createRow = [&](const auto& icon, const auto& label, const auto& value) {
|
fn createRow = [&](const auto& icon, const auto& label, const auto& value) {
|
||||||
return hbox({
|
return hbox(
|
||||||
|
{
|
||||||
text(String(icon)) | color(ui::DEFAULT_THEME.icon),
|
text(String(icon)) | color(ui::DEFAULT_THEME.icon),
|
||||||
text(String(static_cast<CStr>(label))) | color(ui::DEFAULT_THEME.label),
|
text(String(static_cast<CStr>(label))) | color(ui::DEFAULT_THEME.label),
|
||||||
filler(),
|
filler(),
|
||||||
text(String(value)) | color(ui::DEFAULT_THEME.value),
|
text(String(value)) | color(ui::DEFAULT_THEME.value),
|
||||||
text(" "),
|
text(" "),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// System info rows
|
// System info rows
|
||||||
|
@ -130,31 +134,39 @@ namespace {
|
||||||
const WeatherOutput& weatherInfo = data.weather_info.value();
|
const WeatherOutput& weatherInfo = data.weather_info.value();
|
||||||
|
|
||||||
if (weather.show_town_name)
|
if (weather.show_town_name)
|
||||||
content.push_back(hbox({
|
content.push_back(hbox(
|
||||||
|
{
|
||||||
text(String(weatherIcon)) | color(ui::DEFAULT_THEME.icon),
|
text(String(weatherIcon)) | color(ui::DEFAULT_THEME.icon),
|
||||||
text("Weather") | color(ui::DEFAULT_THEME.label),
|
text("Weather") | color(ui::DEFAULT_THEME.label),
|
||||||
filler(),
|
filler(),
|
||||||
|
|
||||||
hbox({
|
hbox(
|
||||||
|
{
|
||||||
text(std::format("{}°F ", std::lround(weatherInfo.main.temp))),
|
text(std::format("{}°F ", std::lround(weatherInfo.main.temp))),
|
||||||
text("in "),
|
text("in "),
|
||||||
text(weatherInfo.name),
|
text(weatherInfo.name),
|
||||||
text(" "),
|
text(" "),
|
||||||
}) |
|
}
|
||||||
|
) |
|
||||||
color(ui::DEFAULT_THEME.value),
|
color(ui::DEFAULT_THEME.value),
|
||||||
}));
|
}
|
||||||
|
));
|
||||||
else
|
else
|
||||||
content.push_back(hbox({
|
content.push_back(hbox(
|
||||||
|
{
|
||||||
text(String(weatherIcon)) | color(ui::DEFAULT_THEME.icon),
|
text(String(weatherIcon)) | color(ui::DEFAULT_THEME.icon),
|
||||||
text("Weather") | color(ui::DEFAULT_THEME.label),
|
text("Weather") | color(ui::DEFAULT_THEME.label),
|
||||||
filler(),
|
filler(),
|
||||||
|
|
||||||
hbox({
|
hbox(
|
||||||
|
{
|
||||||
text(std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
|
text(std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
|
||||||
text(" "),
|
text(" "),
|
||||||
}) |
|
}
|
||||||
|
) |
|
||||||
color(ui::DEFAULT_THEME.value),
|
color(ui::DEFAULT_THEME.value),
|
||||||
}));
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||||
|
@ -196,14 +208,16 @@ namespace {
|
||||||
const String& npText = *nowPlayingResult;
|
const String& npText = *nowPlayingResult;
|
||||||
|
|
||||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||||
content.push_back(hbox({
|
content.push_back(hbox(
|
||||||
|
{
|
||||||
text(String(musicIcon)) | color(ui::DEFAULT_THEME.icon),
|
text(String(musicIcon)) | color(ui::DEFAULT_THEME.icon),
|
||||||
text("Playing") | color(ui::DEFAULT_THEME.label),
|
text("Playing") | color(ui::DEFAULT_THEME.label),
|
||||||
text(" "),
|
text(" "),
|
||||||
filler(),
|
filler(),
|
||||||
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, ui::MAX_PARAGRAPH_LENGTH),
|
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, ui::MAX_PARAGRAPH_LENGTH),
|
||||||
text(" "),
|
text(" "),
|
||||||
}));
|
}
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
const NowPlayingError& error = nowPlayingResult.error();
|
const NowPlayingError& error = nowPlayingResult.error();
|
||||||
|
|
||||||
|
@ -229,6 +243,9 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> i32 {
|
fn main() -> i32 {
|
||||||
|
std::locale::global(std::locale(""));
|
||||||
|
DEBUG_LOG("Global locale set to: {}", std::locale().name());
|
||||||
|
|
||||||
const Config& config = Config::getInstance();
|
const Config& config = Config::getInstance();
|
||||||
const SystemData data = SystemData::fetchSystemData(config);
|
const SystemData data = SystemData::fetchSystemData(config);
|
||||||
|
|
||||||
|
|
112
src/os/linux.cpp
112
src/os/linux.cpp
|
@ -3,8 +3,7 @@
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include <dbus-cxx.h> // needs to be at top for Success/None
|
#include <dbus-cxx.h> // needs to be at top for Success/None
|
||||||
// clang-format on
|
// clang-format on
|
||||||
#include <X11/Xatom.h>
|
#include <cstring>
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
@ -13,6 +12,7 @@
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "src/os/linux/display_guards.h"
|
#include "src/os/linux/display_guards.h"
|
||||||
|
@ -22,77 +22,69 @@ namespace fs = std::filesystem;
|
||||||
using namespace std::string_view_literals;
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using os::linux::DisplayGuard;
|
constexpr auto Trim(StringView sview) -> StringView {
|
||||||
using os::linux::WaylandDisplayGuard;
|
|
||||||
|
|
||||||
constexpr auto Trim(StringView sv) -> StringView {
|
|
||||||
using namespace std::ranges;
|
using namespace std::ranges;
|
||||||
|
|
||||||
constexpr auto isSpace = [](const char character) { return std::isspace(static_cast<unsigned char>(character)); };
|
constexpr auto isSpace = [](const char character) { return std::isspace(static_cast<unsigned char>(character)); };
|
||||||
|
|
||||||
const borrowed_iterator_t<StringView&> start = find_if_not(sv, isSpace);
|
const borrowed_iterator_t<StringView&> start = find_if_not(sview, isSpace);
|
||||||
const borrowed_iterator_t<reverse_view<StringView>> rstart = find_if_not(sv | views::reverse, isSpace);
|
const borrowed_iterator_t<reverse_view<StringView>> rstart = find_if_not(sview | views::reverse, isSpace);
|
||||||
|
|
||||||
return sv.substr(start - sv.begin(), sv.size() - (rstart - sv.rbegin()));
|
return sview.substr(start - sview.begin(), sview.size() - (rstart - sview.rbegin()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetX11WindowManager() -> String {
|
fn GetX11WindowManager() -> String {
|
||||||
const DisplayGuard display;
|
using os::linux::XcbReplyGuard;
|
||||||
|
using os::linux::XorgDisplayGuard;
|
||||||
|
|
||||||
if (!display)
|
const XorgDisplayGuard conn;
|
||||||
|
if (!conn)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
const Atom supportingWmCheck = XInternAtom(display.get(), "_NET_SUPPORTING_WM_CHECK", False);
|
fn internAtom = [&conn](const StringView name) -> XcbReplyGuard<xcb_intern_atom_reply_t> {
|
||||||
const Atom wmName = XInternAtom(display.get(), "_NET_WM_NAME", False);
|
const auto cookie = xcb_intern_atom(conn.get(), 0, static_cast<uint16_t>(name.size()), name.data());
|
||||||
const Atom utf8String = XInternAtom(display.get(), "UTF8_STRING", False);
|
return XcbReplyGuard(xcb_intern_atom_reply(conn.get(), cookie, nullptr));
|
||||||
|
};
|
||||||
|
|
||||||
const Window root = display.defaultRootWindow();
|
const XcbReplyGuard<xcb_intern_atom_reply_t> supportingWmCheck = internAtom("_NET_SUPPORTING_WM_CHECK");
|
||||||
|
const XcbReplyGuard<xcb_intern_atom_reply_t> wmName = internAtom("_NET_WM_NAME");
|
||||||
Atom actualType = 0;
|
const XcbReplyGuard<xcb_intern_atom_reply_t> utf8String = internAtom("UTF8_STRING");
|
||||||
i32 actualFormat = 0;
|
|
||||||
u64 nitems = 0, bytesAfter = 0;
|
|
||||||
u8* data = nullptr;
|
|
||||||
|
|
||||||
if (XGetWindowProperty(
|
|
||||||
display.get(),
|
|
||||||
root,
|
|
||||||
supportingWmCheck,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
False,
|
|
||||||
XA_WINDOW,
|
|
||||||
&actualType,
|
|
||||||
&actualFormat,
|
|
||||||
&nitems,
|
|
||||||
&bytesAfter,
|
|
||||||
&data
|
|
||||||
) == Success &&
|
|
||||||
data) {
|
|
||||||
const UniquePointer<u8, decltype(&XFree)> dataGuard(data, XFree);
|
|
||||||
|
|
||||||
u8* nameData = nullptr;
|
|
||||||
|
|
||||||
if (XGetWindowProperty(
|
|
||||||
display.get(),
|
|
||||||
*reinterpret_cast<Window*>(data),
|
|
||||||
wmName,
|
|
||||||
0,
|
|
||||||
1024,
|
|
||||||
False,
|
|
||||||
utf8String,
|
|
||||||
&actualType,
|
|
||||||
&actualFormat,
|
|
||||||
&nitems,
|
|
||||||
&bytesAfter,
|
|
||||||
&nameData
|
|
||||||
) == Success &&
|
|
||||||
nameData) {
|
|
||||||
const UniquePointer<u8, decltype(&XFree)> nameGuard(nameData, XFree);
|
|
||||||
return reinterpret_cast<char*>(nameData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!supportingWmCheck || !wmName || !utf8String)
|
||||||
return "Unknown (X11)";
|
return "Unknown (X11)";
|
||||||
|
|
||||||
|
const xcb_window_t root = conn.rootScreen()->root;
|
||||||
|
|
||||||
|
fn getProperty = [&conn](
|
||||||
|
const xcb_window_t window,
|
||||||
|
const xcb_atom_t property,
|
||||||
|
const xcb_atom_t type,
|
||||||
|
const uint32_t offset,
|
||||||
|
const uint32_t length
|
||||||
|
) -> XcbReplyGuard<xcb_get_property_reply_t> {
|
||||||
|
const xcb_get_property_cookie_t cookie = xcb_get_property(conn.get(), 0, window, property, type, offset, length);
|
||||||
|
return XcbReplyGuard(xcb_get_property_reply(conn.get(), cookie, nullptr));
|
||||||
|
};
|
||||||
|
|
||||||
|
const XcbReplyGuard<xcb_get_property_reply_t> wmWindowReply =
|
||||||
|
getProperty(root, supportingWmCheck->atom, XCB_ATOM_WINDOW, 0, 1);
|
||||||
|
|
||||||
|
if (!wmWindowReply || wmWindowReply->type != XCB_ATOM_WINDOW || wmWindowReply->format != 32 ||
|
||||||
|
xcb_get_property_value_length(wmWindowReply.get()) == 0)
|
||||||
|
return "Unknown (X11)";
|
||||||
|
|
||||||
|
const xcb_window_t wmWindow = *static_cast<xcb_window_t*>(xcb_get_property_value(wmWindowReply.get()));
|
||||||
|
|
||||||
|
const XcbReplyGuard<xcb_get_property_reply_t> wmNameReply =
|
||||||
|
getProperty(wmWindow, wmName->atom, utf8String->atom, 0, 1024);
|
||||||
|
|
||||||
|
if (!wmNameReply || wmNameReply->type != utf8String->atom || xcb_get_property_value_length(wmNameReply.get()) == 0)
|
||||||
|
return "Unknown (X11)";
|
||||||
|
|
||||||
|
const char* nameData = static_cast<const char*>(xcb_get_property_value(wmNameReply.get()));
|
||||||
|
const usize length = xcb_get_property_value_length(wmNameReply.get());
|
||||||
|
|
||||||
|
return { nameData, length };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ReadProcessCmdline(const i32 pid) -> String {
|
fn ReadProcessCmdline(const i32 pid) -> String {
|
||||||
|
@ -124,6 +116,8 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetWaylandCompositor() -> String {
|
fn GetWaylandCompositor() -> String {
|
||||||
|
using os::linux::WaylandDisplayGuard;
|
||||||
|
|
||||||
if (const Option<String> hypr = DetectHyprlandSpecific())
|
if (const Option<String> hypr = DetectHyprlandSpecific())
|
||||||
return *hypr;
|
return *hypr;
|
||||||
|
|
||||||
|
|
|
@ -7,35 +7,36 @@
|
||||||
#include "src/util/macros.h"
|
#include "src/util/macros.h"
|
||||||
|
|
||||||
namespace os::linux {
|
namespace os::linux {
|
||||||
DisplayGuard::DisplayGuard(const CStr name) : m_Display(XOpenDisplay(name)) {}
|
XorgDisplayGuard::XorgDisplayGuard(const CStr name) : m_Connection(xcb_connect(name, nullptr)) {}
|
||||||
|
|
||||||
DisplayGuard::~DisplayGuard() {
|
XorgDisplayGuard::~XorgDisplayGuard() {
|
||||||
if (m_Display)
|
if (m_Connection)
|
||||||
XCloseDisplay(m_Display);
|
xcb_disconnect(m_Connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayGuard::DisplayGuard(DisplayGuard&& other) noexcept : m_Display(std::exchange(other.m_Display, nullptr)) {}
|
XorgDisplayGuard::XorgDisplayGuard(XorgDisplayGuard&& other) noexcept
|
||||||
|
: m_Connection(std::exchange(other.m_Connection, nullptr)) {}
|
||||||
|
|
||||||
fn DisplayGuard::operator=(DisplayGuard&& other) noexcept -> DisplayGuard& {
|
fn XorgDisplayGuard::operator=(XorgDisplayGuard&& other) noexcept -> XorgDisplayGuard& {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
if (m_Display)
|
if (m_Connection)
|
||||||
XCloseDisplay(m_Display);
|
xcb_disconnect(m_Connection);
|
||||||
|
m_Connection = std::exchange(other.m_Connection, nullptr);
|
||||||
m_Display = std::exchange(other.m_Display, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayGuard::operator bool() const { return m_Display != nullptr; }
|
XorgDisplayGuard::operator bool() const { return m_Connection && !xcb_connection_has_error(m_Connection); }
|
||||||
|
|
||||||
fn DisplayGuard::get() const -> Display* { return m_Display; }
|
fn XorgDisplayGuard::get() const -> xcb_connection_t* { return m_Connection; }
|
||||||
|
|
||||||
fn DisplayGuard::defaultRootWindow() const -> Window {
|
fn XorgDisplayGuard::setup() const -> const xcb_setup_t* {
|
||||||
#pragma clang diagnostic push
|
return m_Connection ? xcb_get_setup(m_Connection) : nullptr;
|
||||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
}
|
||||||
return DefaultRootWindow(m_Display);
|
|
||||||
#pragma clang diagnostic pop
|
fn XorgDisplayGuard::rootScreen() const -> xcb_screen_t* {
|
||||||
|
const xcb_setup_t* setup = this->setup();
|
||||||
|
return setup ? xcb_setup_roots_iterator(setup).data : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaylandDisplayGuard::WaylandDisplayGuard() : m_Display(wl_display_connect(nullptr)) {}
|
WaylandDisplayGuard::WaylandDisplayGuard() : m_Display(wl_display_connect(nullptr)) {}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "src/util/macros.h"
|
#include "src/util/macros.h"
|
||||||
|
|
||||||
|
@ -12,28 +12,70 @@ namespace os::linux {
|
||||||
* RAII wrapper for X11 Display connections
|
* RAII wrapper for X11 Display connections
|
||||||
* Automatically handles resource acquisition and cleanup
|
* Automatically handles resource acquisition and cleanup
|
||||||
*/
|
*/
|
||||||
class DisplayGuard {
|
class XorgDisplayGuard {
|
||||||
Display* m_Display;
|
xcb_connection_t* m_Connection = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Opens an X11 display connection
|
* Opens an XCB connection
|
||||||
* @param name Display name (nullptr for default)
|
* @param name Display name (nullptr for default)
|
||||||
*/
|
*/
|
||||||
explicit DisplayGuard(CStr name = nullptr);
|
explicit XorgDisplayGuard(CStr name = nullptr);
|
||||||
~DisplayGuard();
|
~XorgDisplayGuard();
|
||||||
|
|
||||||
// Non-copyable
|
// Non-copyable
|
||||||
DisplayGuard(const DisplayGuard&) = delete;
|
XorgDisplayGuard(const XorgDisplayGuard&) = delete;
|
||||||
fn operator=(const DisplayGuard&)->DisplayGuard& = delete;
|
fn operator=(const XorgDisplayGuard&)->XorgDisplayGuard& = delete;
|
||||||
|
|
||||||
// Movable
|
// Movable
|
||||||
DisplayGuard(DisplayGuard&& other) noexcept;
|
XorgDisplayGuard(XorgDisplayGuard&& other) noexcept;
|
||||||
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard&;
|
fn operator=(XorgDisplayGuard&& other) noexcept -> XorgDisplayGuard&;
|
||||||
|
|
||||||
[[nodiscard]] explicit operator bool() const;
|
[[nodiscard]] explicit operator bool() const;
|
||||||
[[nodiscard]] fn get() const -> Display*;
|
|
||||||
[[nodiscard]] fn defaultRootWindow() const -> Window;
|
[[nodiscard]] fn get() const -> xcb_connection_t*;
|
||||||
|
[[nodiscard]] fn setup() const -> const xcb_setup_t*;
|
||||||
|
[[nodiscard]] fn rootScreen() const -> xcb_screen_t*;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RAII wrapper for XCB replies
|
||||||
|
* Handles automatic cleanup of various XCB reply objects
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class XcbReplyGuard {
|
||||||
|
T* m_Reply = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
XcbReplyGuard() = default;
|
||||||
|
explicit XcbReplyGuard(T* reply) : m_Reply(reply) {}
|
||||||
|
|
||||||
|
~XcbReplyGuard() {
|
||||||
|
if (m_Reply)
|
||||||
|
free(m_Reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-copyable
|
||||||
|
XcbReplyGuard(const XcbReplyGuard&) = delete;
|
||||||
|
fn operator=(const XcbReplyGuard&)->XcbReplyGuard& = delete;
|
||||||
|
|
||||||
|
// Movable
|
||||||
|
XcbReplyGuard(XcbReplyGuard&& other) noexcept : m_Reply(std::exchange(other.m_Reply, nullptr)) {}
|
||||||
|
fn operator=(XcbReplyGuard&& other) noexcept -> XcbReplyGuard& {
|
||||||
|
if (this != &other) {
|
||||||
|
if (m_Reply)
|
||||||
|
free(m_Reply);
|
||||||
|
|
||||||
|
m_Reply = std::exchange(other.m_Reply, nullptr);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +101,7 @@ namespace os::linux {
|
||||||
fn operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard&;
|
fn operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard&;
|
||||||
|
|
||||||
[[nodiscard]] explicit operator bool() const;
|
[[nodiscard]] explicit operator bool() const;
|
||||||
|
|
||||||
[[nodiscard]] fn get() const -> wl_display*;
|
[[nodiscard]] fn get() const -> wl_display*;
|
||||||
[[nodiscard]] fn fd() const -> i32;
|
[[nodiscard]] fn fd() const -> i32;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
// ReSharper disable CppDFAConstantParameter
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// Fixes conflict in Windows with <windows.h>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#undef ERROR
|
#undef ERROR
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,139 +15,213 @@
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
/// Macro alias for trailing return type functions.
|
||||||
#define fn auto
|
#define fn auto
|
||||||
|
|
||||||
#ifdef None
|
/// Macro alias for std::nullopt, represents an empty optional value.
|
||||||
#undef None
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define None std::nullopt
|
#define None std::nullopt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace term
|
||||||
|
* @brief Provides terminal-related utilities, including color and style formatting.
|
||||||
|
*/
|
||||||
namespace term {
|
namespace term {
|
||||||
enum class Emphasis : u8 { none = 0, bold = 1, italic = 2 };
|
/**
|
||||||
|
* @enum Emphasis
|
||||||
|
* @brief Represents text emphasis styles.
|
||||||
|
*
|
||||||
|
* Enum values can be combined using bitwise OR to apply multiple styles at once.
|
||||||
|
*/
|
||||||
|
enum class Emphasis : u8 {
|
||||||
|
Bold, ///< Bold text.
|
||||||
|
Italic ///< Italic text.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum Color
|
||||||
|
* @brief Represents ANSI color codes for terminal output.
|
||||||
|
*
|
||||||
|
* Color codes can be used to format terminal output.
|
||||||
|
*/
|
||||||
|
enum class Color : u8 {
|
||||||
|
Black = 30, ///< Black color.
|
||||||
|
Red = 31, ///< Red color.
|
||||||
|
Green = 32, ///< Green color.
|
||||||
|
Yellow = 33, ///< Yellow color.
|
||||||
|
Blue = 34, ///< Blue color.
|
||||||
|
Magenta = 35, ///< Magenta color.
|
||||||
|
Cyan = 36, ///< Cyan color.
|
||||||
|
White = 37, ///< White color.
|
||||||
|
BrightBlack = 90, ///< Bright black (gray) color.
|
||||||
|
BrightRed = 91, ///< Bright red color.
|
||||||
|
BrightGreen = 92, ///< Bright green color.
|
||||||
|
BrightYellow = 93, ///< Bright yellow color.
|
||||||
|
BrightBlue = 94, ///< Bright blue color.
|
||||||
|
BrightMagenta = 95, ///< Bright magenta color.
|
||||||
|
BrightCyan = 96, ///< Bright cyan color.
|
||||||
|
BrightWhite = 97, ///< Bright white color.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Combines two emphasis styles using bitwise OR.
|
||||||
|
* @param emphA The first emphasis style.
|
||||||
|
* @param emphB The second emphasis style.
|
||||||
|
* @return The combined emphasis style.
|
||||||
|
*/
|
||||||
constexpr fn operator|(Emphasis emphA, Emphasis emphB)->Emphasis {
|
constexpr fn operator|(Emphasis emphA, Emphasis emphB)->Emphasis {
|
||||||
return static_cast<Emphasis>(static_cast<int>(emphA) | static_cast<int>(emphB));
|
return static_cast<Emphasis>(static_cast<u8>(emphA) | static_cast<u8>(emphB));
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
/**
|
||||||
enum class Color : u8 {
|
* @brief Checks if two emphasis styles are equal using bitwise AND.
|
||||||
black [[maybe_unused]] = 30,
|
* @param emphA The first emphasis style.
|
||||||
red [[maybe_unused]] = 31,
|
* @param emphB The second emphasis style.
|
||||||
green [[maybe_unused]] = 32,
|
* @return The result of the bitwise AND operation.
|
||||||
yellow [[maybe_unused]] = 33,
|
*/
|
||||||
blue [[maybe_unused]] = 34,
|
constexpr fn operator&(Emphasis emphA, Emphasis emphB)->u8 { return static_cast<u8>(emphA) & static_cast<u8>(emphB); }
|
||||||
magenta [[maybe_unused]] = 35,
|
|
||||||
cyan [[maybe_unused]] = 36,
|
|
||||||
white [[maybe_unused]] = 37,
|
|
||||||
bright_black [[maybe_unused]] = 90,
|
|
||||||
bright_red [[maybe_unused]] = 91,
|
|
||||||
bright_green [[maybe_unused]] = 92,
|
|
||||||
bright_yellow [[maybe_unused]] = 93,
|
|
||||||
bright_blue [[maybe_unused]] = 94,
|
|
||||||
bright_magenta [[maybe_unused]] = 95,
|
|
||||||
bright_cyan [[maybe_unused]] = 96,
|
|
||||||
bright_white [[maybe_unused]] = 97
|
|
||||||
};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
struct FgColor {
|
|
||||||
Color col;
|
|
||||||
|
|
||||||
constexpr explicit FgColor(const Color color) : col(color) {}
|
|
||||||
|
|
||||||
[[nodiscard]] fn ansiCode() const -> String { return std::format("\033[{}m", static_cast<int>(col)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct Style
|
||||||
|
* @brief Represents a combination of text styles.
|
||||||
|
*
|
||||||
|
* Emphasis and color are both optional, allowing for flexible styling.
|
||||||
|
*/
|
||||||
struct Style {
|
struct Style {
|
||||||
Emphasis emph = Emphasis::none;
|
Option<Emphasis> emph; ///< Optional emphasis style.
|
||||||
FgColor fg_col = FgColor(static_cast<Color>(-1));
|
Option<Color> fg_col; ///< Optional foreground color style.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generates the ANSI escape code for the combined styles.
|
||||||
|
* @return The ANSI escape code for the combined styles.
|
||||||
|
*/
|
||||||
[[nodiscard]] fn ansiCode() const -> String {
|
[[nodiscard]] fn ansiCode() const -> String {
|
||||||
String result;
|
String result;
|
||||||
|
|
||||||
if (emph != Emphasis::none) {
|
if (emph) {
|
||||||
if ((static_cast<int>(emph) & static_cast<int>(Emphasis::bold)) != 0)
|
if ((*emph & Emphasis::Bold) != 0)
|
||||||
result += "\033[1m";
|
result += "\033[1m";
|
||||||
if ((static_cast<int>(emph) & static_cast<int>(Emphasis::italic)) != 0)
|
if ((*emph & Emphasis::Italic) != 0)
|
||||||
result += "\033[3m";
|
result += "\033[3m";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<int>(fg_col.col) != -1)
|
if (fg_col)
|
||||||
result += fg_col.ansiCode();
|
result += std::format("\033[{}m", static_cast<u8>(*fg_col));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr fn operator|(const Emphasis emph, const FgColor fgColor)->Style {
|
/**
|
||||||
|
* @brief Combines an emphasis style and a foreground color into a Style.
|
||||||
|
* @param emph The emphasis style to apply.
|
||||||
|
* @param fgColor The foreground color to apply.
|
||||||
|
* @return The combined style.
|
||||||
|
*/
|
||||||
|
constexpr fn operator|(const Emphasis emph, const Color fgColor)->Style {
|
||||||
return { .emph = emph, .fg_col = fgColor };
|
return { .emph = emph, .fg_col = fgColor };
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr fn operator|(const FgColor fgColor, const Emphasis emph)->Style {
|
/**
|
||||||
|
* @brief Combines a foreground color and an emphasis style into a Style.
|
||||||
|
* @param fgColor The foreground color to apply.
|
||||||
|
* @param emph The emphasis style to apply.
|
||||||
|
* @return The combined style.
|
||||||
|
*/
|
||||||
|
constexpr fn operator|(const Color fgColor, const Emphasis emph)->Style {
|
||||||
return { .emph = emph, .fg_col = fgColor };
|
return { .emph = emph, .fg_col = fgColor };
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr CStr reset = "\033[0m";
|
/**
|
||||||
|
* @brief Prints formatted text with the specified style.
|
||||||
|
* @tparam Args Parameter pack for format arguments.
|
||||||
|
* @param style The Style object containing emphasis and/or color.
|
||||||
|
* @param fmt The format string.
|
||||||
|
* @param args The arguments for the format string.
|
||||||
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
fn Print(const Style& style, std::format_string<Args...> fmt, Args&&... args) -> void {
|
fn Print(const Style& style, std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||||
std::print("{}{}{}", style.ansiCode(), std::format(fmt, std::forward<Args>(args)...), reset);
|
if (const String styleCode = style.ansiCode(); styleCode.empty())
|
||||||
|
std::print(fmt, std::forward<Args>(args)...);
|
||||||
|
else
|
||||||
|
std::print("{}{}{}", styleCode, std::format(fmt, std::forward<Args>(args)...), "\033[0m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints formatted text with the specified foreground color.
|
||||||
|
* @tparam Args Parameter pack for format arguments.
|
||||||
|
* @param fgColor The foreground color to apply.
|
||||||
|
* @param fmt The format string.
|
||||||
|
* @param args The arguments for the format string.
|
||||||
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
fn Print(const FgColor& fgColor, std::format_string<Args...> fmt, Args&&... args) -> void {
|
fn Print(const Color& fgColor, std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||||
std::print("{}{}{}", fgColor.ansiCode(), std::format(fmt, std::forward<Args>(args)...), reset);
|
Print({ .emph = None, .fg_col = fgColor }, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints formatted text with the specified emphasis style.
|
||||||
|
* @tparam Args Parameter pack for format arguments.
|
||||||
|
* @param emph The emphasis style to apply.
|
||||||
|
* @param fmt The format string.
|
||||||
|
* @param args The arguments for the format string.
|
||||||
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
fn Print(Emphasis emph, std::format_string<Args...> fmt, Args&&... args) -> void {
|
fn Print(const Emphasis emph, std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||||
Print({ .emph = emph }, fmt, std::forward<Args>(args)...);
|
Print({ .emph = emph, .fg_col = None }, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints formatted text with no specific style (default terminal style).
|
||||||
|
* @tparam Args Parameter pack for format arguments.
|
||||||
|
* @param fmt The format string.
|
||||||
|
* @param args The arguments for the format string.
|
||||||
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
fn Print(std::format_string<Args...> fmt, Args&&... args) -> void {
|
fn Print(std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||||
|
// Directly use std::print for unstyled output
|
||||||
std::print(fmt, std::forward<Args>(args)...);
|
std::print(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace log_colors {
|
/**
|
||||||
using term::Color;
|
* @enum LogLevel
|
||||||
|
* @brief Represents different log levels.
|
||||||
constexpr Color debug = Color::cyan, info = Color::green, warn = Color::yellow, error = Color::red,
|
*/
|
||||||
timestamp = Color::bright_white, file_info = Color::bright_white;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR };
|
enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Logs a message with the specified log level, source location, and format string.
|
||||||
|
* @tparam Args Parameter pack for format arguments.
|
||||||
|
* @param level The log level (DEBUG, INFO, WARN, ERROR).
|
||||||
|
* @param loc The source location of the log message.
|
||||||
|
* @param fmt The format string.
|
||||||
|
* @param args The arguments for the format string.
|
||||||
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_string<Args...> fmt, Args&&... args)
|
fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_string<Args...> fmt, Args&&... args) {
|
||||||
-> void {
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
using namespace term;
|
||||||
const time_point<system_clock, duration<i64>> now = std::chrono::floor<seconds>(system_clock::now());
|
using enum Color;
|
||||||
|
|
||||||
const auto [color, levelStr] = [&] {
|
const auto [color, levelStr] = [&] {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LogLevel::DEBUG: return std::make_pair(log_colors::debug, "DEBUG");
|
case LogLevel::DEBUG: return std::make_pair(Cyan, "DEBUG");
|
||||||
case LogLevel::INFO: return std::make_pair(log_colors::info, "INFO ");
|
case LogLevel::INFO: return std::make_pair(Green, "INFO ");
|
||||||
case LogLevel::WARN: return std::make_pair(log_colors::warn, "WARN ");
|
case LogLevel::WARN: return std::make_pair(Yellow, "WARN ");
|
||||||
case LogLevel::ERROR: return std::make_pair(log_colors::error, "ERROR");
|
case LogLevel::ERROR: return std::make_pair(Red, "ERROR");
|
||||||
default: std::unreachable();
|
default: std::unreachable();
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const String filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
|
const String filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
|
||||||
|
|
||||||
using namespace term;
|
Print(BrightWhite, "[{:%X}] ", zoned_time { current_zone(), std::chrono::floor<seconds>(system_clock::now()) });
|
||||||
|
Print(Emphasis::Bold | color, "{} ", levelStr);
|
||||||
Print(FgColor(log_colors::timestamp), "[{:%H:%M:%S}] ", now);
|
|
||||||
Print(Emphasis::bold | FgColor(color), "{} ", levelStr);
|
|
||||||
Print(fmt, std::forward<Args>(args)...);
|
Print(fmt, std::forward<Args>(args)...);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
Print(FgColor(log_colors::file_info), "\n{:>14} ", "╰──");
|
Print(BrightWhite, "\n{:>14} ", "╰──");
|
||||||
Print(Emphasis::italic | FgColor(log_colors::file_info), "{}:{}", filename, loc.line());
|
Print(Emphasis::Italic | BrightWhite, "{}:{}", filename, loc.line());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Print("\n");
|
Print("\n");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue