suckle
This commit is contained in:
parent
2219182539
commit
9695ceec8a
13 changed files with 261 additions and 262 deletions
|
@ -12,6 +12,7 @@ Checks: >
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
-cppcoreguidelines-owning-memory,
|
-cppcoreguidelines-owning-memory,
|
||||||
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-cppcoreguidelines-pro-type-member-init,
|
-cppcoreguidelines-pro-type-member-init,
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
-fuchsia-*,
|
-fuchsia-*,
|
||||||
|
|
|
@ -89,7 +89,7 @@ add_project_arguments(common_cpp_args, language : 'cpp')
|
||||||
# ------- #
|
# ------- #
|
||||||
# Files #
|
# Files #
|
||||||
# ------- #
|
# ------- #
|
||||||
base_sources = files('src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp')
|
base_sources = files('src/core/system_data.cpp', 'src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp')
|
||||||
|
|
||||||
platform_sources = {
|
platform_sources = {
|
||||||
'linux' : ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp', 'src/os/linux/display_guards.cpp'],
|
'linux' : ['src/os/linux.cpp', 'src/os/linux/issetugid_stub.cpp', 'src/os/linux/display_guards.cpp'],
|
||||||
|
|
|
@ -74,13 +74,13 @@ namespace {
|
||||||
|
|
||||||
String defaultName = GetUserNameA(username.data(), &size) ? username.data() : "User";
|
String defaultName = GetUserNameA(username.data(), &size) ? username.data() : "User";
|
||||||
#else
|
#else
|
||||||
const struct passwd* pwd = getpwuid(getuid());
|
const passwd* pwd = getpwuid(getuid());
|
||||||
CStr pwdName = pwd ? pwd->pw_name : nullptr;
|
CStr pwdName = pwd ? pwd->pw_name : nullptr;
|
||||||
|
|
||||||
const Result<String, EnvError> envUser = GetEnv("USER");
|
const Result<String, EnvError> envUser = GetEnv("USER");
|
||||||
const Result<String, EnvError> envLogname = GetEnv("LOGNAME");
|
const Result<String, EnvError> envLogname = GetEnv("LOGNAME");
|
||||||
|
|
||||||
String defaultName = (pwdName) ? pwdName : (envUser) ? *envUser : (envLogname) ? *envLogname : "User";
|
String defaultName = pwdName ? pwdName : envUser ? *envUser : envLogname ? *envLogname : "User";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
toml::table* general = root.insert("general", toml::table {}).first->second.as_table();
|
toml::table* general = root.insert("general", toml::table {}).first->second.as_table();
|
||||||
|
|
|
@ -21,7 +21,7 @@ struct General {
|
||||||
DWORD size = sizeof(username);
|
DWORD size = sizeof(username);
|
||||||
return GetUserNameA(username.data(), &size) ? username.data() : "User";
|
return GetUserNameA(username.data(), &size) ? username.data() : "User";
|
||||||
#else
|
#else
|
||||||
if (struct passwd* pwd = getpwuid(getuid()))
|
if (const passwd* pwd = getpwuid(getuid()))
|
||||||
return pwd->pw_name;
|
return pwd->pw_name;
|
||||||
|
|
||||||
if (Result<String, EnvError> envUser = GetEnv("USER"))
|
if (Result<String, EnvError> envUser = GetEnv("USER"))
|
||||||
|
@ -55,7 +55,7 @@ struct Weather {
|
||||||
static fn fromToml(const toml::table& tbl) -> Weather {
|
static fn fromToml(const toml::table& tbl) -> Weather {
|
||||||
Weather weather;
|
Weather weather;
|
||||||
|
|
||||||
Option<String> apiKey = tbl["api_key"].value<String>();
|
const Option<String> apiKey = tbl["api_key"].value<String>();
|
||||||
|
|
||||||
weather.enabled = tbl["enabled"].value_or<bool>(false) && apiKey;
|
weather.enabled = tbl["enabled"].value_or<bool>(false) && apiKey;
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace {
|
||||||
DEBUG_LOG("Reading from cache file...");
|
DEBUG_LOG("Reading from cache file...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
const String content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator<char>());
|
||||||
WeatherOutput result;
|
WeatherOutput result;
|
||||||
|
|
||||||
if (const glz::error_ctx errc = glz::read<glaze_opts>(result, content); errc.ec != glz::error_code::none)
|
if (const glz::error_ctx errc = glz::read<glaze_opts>(result, content); errc.ec != glz::error_code::none)
|
||||||
|
|
64
src/core/system_data.cpp
Normal file
64
src/core/system_data.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
#include "system_data.h"
|
||||||
|
|
||||||
|
#include "src/config/config.h"
|
||||||
|
#include "src/os/os.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
fn GetDate() -> String {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
const year_month_day ymd = year_month_day { floor<days>(system_clock::now()) };
|
||||||
|
|
||||||
|
String month = std::format("{:%B}", ymd);
|
||||||
|
|
||||||
|
u32 day = static_cast<u32>(ymd.day());
|
||||||
|
|
||||||
|
CStr suffix = day >= 11 && day <= 13 ? "th"
|
||||||
|
: day % 10 == 1 ? "st"
|
||||||
|
: day % 10 == 2 ? "nd"
|
||||||
|
: day % 10 == 3 ? "rd"
|
||||||
|
: "th";
|
||||||
|
|
||||||
|
return std::format("{} {}{}", month, day, suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn SystemData::fetchSystemData(const Config& config) -> SystemData {
|
||||||
|
SystemData data {
|
||||||
|
.date = GetDate(),
|
||||||
|
.host = os::GetHost(),
|
||||||
|
.kernel_version = os::GetKernelVersion(),
|
||||||
|
.os_version = os::GetOSVersion(),
|
||||||
|
.mem_info = os::GetMemInfo(),
|
||||||
|
.desktop_environment = os::GetDesktopEnvironment(),
|
||||||
|
.window_manager = os::GetWindowManager(),
|
||||||
|
};
|
||||||
|
|
||||||
|
auto diskShell = std::async(std::launch::async, [] {
|
||||||
|
auto [used, total] = os::GetDiskUsage();
|
||||||
|
return std::make_tuple(used, total, os::GetShell());
|
||||||
|
});
|
||||||
|
|
||||||
|
std::future<WeatherOutput> weather;
|
||||||
|
std::future<Result<String, NowPlayingError>> nowPlaying;
|
||||||
|
|
||||||
|
if (config.weather.enabled)
|
||||||
|
weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); });
|
||||||
|
|
||||||
|
if (config.now_playing.enabled)
|
||||||
|
nowPlaying = std::async(std::launch::async, os::GetNowPlaying);
|
||||||
|
|
||||||
|
auto [used, total, shell] = diskShell.get();
|
||||||
|
data.disk_used = used;
|
||||||
|
data.disk_total = total;
|
||||||
|
data.shell = shell;
|
||||||
|
|
||||||
|
if (weather.valid())
|
||||||
|
data.weather_info = weather.get();
|
||||||
|
if (nowPlaying.valid())
|
||||||
|
data.now_playing = nowPlaying.get();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
36
src/core/system_data.h
Normal file
36
src/core/system_data.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/config/config.h"
|
||||||
|
#include "src/util/types.h"
|
||||||
|
|
||||||
|
struct BytesToGiB {
|
||||||
|
u64 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u64 GIB = 1'073'741'824;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::formatter<BytesToGiB> : std::formatter<double> {
|
||||||
|
fn format(const BytesToGiB& BTG, auto& ctx) const {
|
||||||
|
return std::format_to(ctx.out(), "{:.2f}GiB", static_cast<f64>(BTG.value) / GIB);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Structure to hold the collected system data
|
||||||
|
struct SystemData {
|
||||||
|
String date;
|
||||||
|
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
|
||||||
|
static fn fetchSystemData(const Config& config) -> SystemData;
|
||||||
|
};
|
126
src/main.cpp
126
src/main.cpp
|
@ -8,24 +8,11 @@
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
#include "core/system_data.h"
|
||||||
#include "os/os.h"
|
#include "os/os.h"
|
||||||
|
|
||||||
constexpr inline bool SHOW_ICONS = true;
|
constexpr inline bool SHOW_ICONS = true;
|
||||||
|
|
||||||
struct BytesToGiB {
|
|
||||||
u64 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1024^3 (size of 1 GiB)
|
|
||||||
constexpr u64 GIB = 1'073'741'824;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct std::formatter<BytesToGiB> : std::formatter<double> {
|
|
||||||
fn format(const BytesToGiB& BTG, auto& ctx) const {
|
|
||||||
return std::format_to(ctx.out(), "{:.2f}GiB", static_cast<f64>(BTG.value) / GIB);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
using ftxui::Color;
|
using ftxui::Color;
|
||||||
|
|
||||||
|
@ -99,93 +86,6 @@ namespace ui {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <typename T, typename E, typename ValueFunc, typename ErrorFunc>
|
|
||||||
fn visit_result(const Result<T, E>& exp, ValueFunc value_func, ErrorFunc error_func) {
|
|
||||||
if (exp.has_value())
|
|
||||||
return value_func(*exp);
|
|
||||||
|
|
||||||
return error_func(exp.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn GetDate() -> String {
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
const year_month_day ymd = year_month_day { floor<days>(system_clock::now()) };
|
|
||||||
|
|
||||||
String month = std::format("{:%B}", ymd);
|
|
||||||
|
|
||||||
u32 day = static_cast<u32>(ymd.day());
|
|
||||||
|
|
||||||
CStr suffix = static_cast<CStr>(
|
|
||||||
(day >= 11 && day <= 13) ? "th"
|
|
||||||
: (day % 10 == 1) ? "st"
|
|
||||||
: (day % 10 == 2) ? "nd"
|
|
||||||
: (day % 10 == 3) ? "rd"
|
|
||||||
: "th"
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::format("{} {}{}", month, day, suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SystemData {
|
|
||||||
String date;
|
|
||||||
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 fn fetchSystemData(const Config& config) -> SystemData {
|
|
||||||
SystemData data;
|
|
||||||
|
|
||||||
// Single-threaded execution for core system info (faster on Windows)
|
|
||||||
data.date = GetDate();
|
|
||||||
data.host = os::GetHost();
|
|
||||||
data.kernel_version = os::GetKernelVersion();
|
|
||||||
data.os_version = os::GetOSVersion();
|
|
||||||
data.mem_info = os::GetMemInfo();
|
|
||||||
|
|
||||||
// Desktop environment info
|
|
||||||
data.desktop_environment = os::GetDesktopEnvironment();
|
|
||||||
data.window_manager = os::GetWindowManager();
|
|
||||||
|
|
||||||
// Parallel execution for disk/shell only
|
|
||||||
auto diskShell = std::async(std::launch::async, [] {
|
|
||||||
auto [used, total] = os::GetDiskUsage();
|
|
||||||
return std::make_tuple(used, total, os::GetShell());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Conditional tasks
|
|
||||||
std::future<WeatherOutput> weather;
|
|
||||||
std::future<Result<String, NowPlayingError>> nowPlaying;
|
|
||||||
|
|
||||||
if (config.weather.enabled)
|
|
||||||
weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); });
|
|
||||||
|
|
||||||
if (config.now_playing.enabled)
|
|
||||||
nowPlaying = std::async(std::launch::async, os::GetNowPlaying);
|
|
||||||
|
|
||||||
// Get remaining results
|
|
||||||
auto [used, total, shell] = diskShell.get();
|
|
||||||
data.disk_used = used;
|
|
||||||
data.disk_total = total;
|
|
||||||
data.shell = shell;
|
|
||||||
|
|
||||||
if (weather.valid())
|
|
||||||
data.weather_info = weather.get();
|
|
||||||
if (nowPlaying.valid())
|
|
||||||
data.now_playing = nowPlaying.get();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using namespace ftxui;
|
using namespace ftxui;
|
||||||
|
|
||||||
fn CreateColorCircles() -> Element {
|
fn CreateColorCircles() -> Element {
|
||||||
|
@ -198,12 +98,12 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn SystemInfoBox(const Config& config, const SystemData& data) -> Element {
|
fn SystemInfoBox(const Config& config, const SystemData& data) -> Element {
|
||||||
// Fetch data
|
|
||||||
const String& name = config.general.name;
|
const String& name = config.general.name;
|
||||||
const Weather weather = config.weather;
|
const Weather weather = config.weather;
|
||||||
const bool nowPlayingEnabled = config.now_playing.enabled;
|
const bool nowPlayingEnabled = config.now_playing.enabled;
|
||||||
|
|
||||||
const auto& [userIcon, paletteIcon, calendarIcon, hostIcon, kernelIcon, osIcon, memoryIcon, weatherIcon, musicIcon, diskIcon, shellIcon, deIcon, wmIcon] =
|
const auto& [userIcon, paletteIcon, calendarIcon, hostIcon, kernelIcon, osIcon, memoryIcon, weatherIcon, musicIcon, diskIcon, shellIcon, deIcon, wmIcon] =
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
SHOW_ICONS ? ui::NERD_ICONS : ui::EMPTY_ICONS;
|
SHOW_ICONS ? ui::NERD_ICONS : ui::EMPTY_ICONS;
|
||||||
|
|
||||||
Elements content;
|
Elements content;
|
||||||
|
@ -282,17 +182,15 @@ namespace {
|
||||||
if (!data.kernel_version.empty())
|
if (!data.kernel_version.empty())
|
||||||
content.push_back(createRow(kernelIcon, "Kernel", data.kernel_version));
|
content.push_back(createRow(kernelIcon, "Kernel", data.kernel_version));
|
||||||
|
|
||||||
visit_result(
|
if (data.os_version)
|
||||||
data.os_version,
|
content.push_back(createRow(String(osIcon), "OS", *data.os_version));
|
||||||
[&](const String& version) { content.push_back(createRow(String(osIcon), "OS", version)); },
|
else
|
||||||
[](const String& error) { ERROR_LOG("Failed to get OS version: {}", error); }
|
ERROR_LOG("Failed to get OS version: {}", data.os_version.error());
|
||||||
);
|
|
||||||
|
|
||||||
visit_result(
|
if (data.mem_info)
|
||||||
data.mem_info,
|
content.push_back(createRow(memoryIcon, "RAM", std::format("{}", BytesToGiB { *data.mem_info })));
|
||||||
[&](const u64& mem) { content.push_back(createRow(memoryIcon, "RAM", std::format("{}", BytesToGiB { mem }))); },
|
else
|
||||||
[](const String& error) { ERROR_LOG("Failed to get memory info: {}", error); }
|
ERROR_LOG("Failed to get memory info: {}", data.mem_info.error());
|
||||||
);
|
|
||||||
|
|
||||||
// Add Disk usage row
|
// Add Disk usage row
|
||||||
content.push_back(
|
content.push_back(
|
||||||
|
@ -303,14 +201,14 @@ namespace {
|
||||||
|
|
||||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||||
|
|
||||||
if (data.desktop_environment.has_value() && *data.desktop_environment != data.window_manager)
|
if (data.desktop_environment && *data.desktop_environment != data.window_manager)
|
||||||
content.push_back(createRow(deIcon, "DE", *data.desktop_environment));
|
content.push_back(createRow(deIcon, "DE", *data.desktop_environment));
|
||||||
|
|
||||||
if (!data.window_manager.empty())
|
if (!data.window_manager.empty())
|
||||||
content.push_back(createRow(wmIcon, "WM", data.window_manager));
|
content.push_back(createRow(wmIcon, "WM", data.window_manager));
|
||||||
|
|
||||||
// Now Playing row
|
// Now Playing row
|
||||||
if (nowPlayingEnabled && data.now_playing.has_value()) {
|
if (nowPlayingEnabled && data.now_playing) {
|
||||||
if (const Result<String, NowPlayingError>& nowPlayingResult = *data.now_playing; nowPlayingResult.has_value()) {
|
if (const Result<String, NowPlayingError>& nowPlayingResult = *data.now_playing; nowPlayingResult.has_value()) {
|
||||||
const String& npText = *nowPlayingResult;
|
const String& npText = *nowPlayingResult;
|
||||||
|
|
||||||
|
|
179
src/os/linux.cpp
179
src/os/linux.cpp
|
@ -6,6 +6,7 @@
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <ranges>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
|
@ -18,24 +19,35 @@
|
||||||
#include "src/util/macros.h"
|
#include "src/util/macros.h"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using os::linux::DisplayGuard;
|
using os::linux::DisplayGuard;
|
||||||
using os::linux::WaylandDisplayGuard;
|
using os::linux::WaylandDisplayGuard;
|
||||||
|
|
||||||
|
constexpr auto Trim(StringView sv) -> StringView {
|
||||||
|
using namespace std::ranges;
|
||||||
|
|
||||||
|
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<reverse_view<StringView>> rstart = find_if_not(sv | views::reverse, isSpace);
|
||||||
|
|
||||||
|
return sv.substr(start - sv.begin(), sv.size() - (rstart - sv.rbegin()));
|
||||||
|
}
|
||||||
|
|
||||||
fn GetX11WindowManager() -> String {
|
fn GetX11WindowManager() -> String {
|
||||||
DisplayGuard display;
|
const DisplayGuard display;
|
||||||
|
|
||||||
if (!display)
|
if (!display)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
Atom supportingWmCheck = XInternAtom(display.get(), "_NET_SUPPORTING_WM_CHECK", False);
|
const Atom supportingWmCheck = XInternAtom(display.get(), "_NET_SUPPORTING_WM_CHECK", False);
|
||||||
Atom wmName = XInternAtom(display.get(), "_NET_WM_NAME", False);
|
const Atom wmName = XInternAtom(display.get(), "_NET_WM_NAME", False);
|
||||||
Atom utf8String = XInternAtom(display.get(), "UTF8_STRING", False);
|
const Atom utf8String = XInternAtom(display.get(), "UTF8_STRING", False);
|
||||||
|
|
||||||
Window root = display.defaultRootWindow();
|
const Window root = display.defaultRootWindow();
|
||||||
|
|
||||||
Window wmWindow = 0;
|
|
||||||
Atom actualType = 0;
|
Atom actualType = 0;
|
||||||
i32 actualFormat = 0;
|
i32 actualFormat = 0;
|
||||||
u64 nitems = 0, bytesAfter = 0;
|
u64 nitems = 0, bytesAfter = 0;
|
||||||
|
@ -56,14 +68,13 @@ namespace {
|
||||||
&data
|
&data
|
||||||
) == Success &&
|
) == Success &&
|
||||||
data) {
|
data) {
|
||||||
UniquePointer<u8, decltype(&XFree)> dataGuard(data, XFree);
|
const UniquePointer<u8, decltype(&XFree)> dataGuard(data, XFree);
|
||||||
wmWindow = *std::bit_cast<Window*>(data);
|
|
||||||
|
|
||||||
u8* nameData = nullptr;
|
u8* nameData = nullptr;
|
||||||
|
|
||||||
if (XGetWindowProperty(
|
if (XGetWindowProperty(
|
||||||
display.get(),
|
display.get(),
|
||||||
wmWindow,
|
*reinterpret_cast<Window*>(data),
|
||||||
wmName,
|
wmName,
|
||||||
0,
|
0,
|
||||||
1024,
|
1024,
|
||||||
|
@ -76,19 +87,18 @@ namespace {
|
||||||
&nameData
|
&nameData
|
||||||
) == Success &&
|
) == Success &&
|
||||||
nameData) {
|
nameData) {
|
||||||
UniquePointer<u8, decltype(&XFree)> nameGuard(nameData, XFree);
|
const UniquePointer<u8, decltype(&XFree)> nameGuard(nameData, XFree);
|
||||||
return std::bit_cast<char*>(nameData);
|
return reinterpret_cast<char*>(nameData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Unknown (X11)";
|
return "Unknown (X11)";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ReadProcessCmdline(i32 pid) -> String {
|
fn ReadProcessCmdline(const i32 pid) -> String {
|
||||||
std::ifstream cmdlineFile("/proc/" + std::to_string(pid) + "/cmdline");
|
std::ifstream cmdlineFile("/proc/" + std::to_string(pid) + "/cmdline");
|
||||||
String cmdline;
|
|
||||||
|
|
||||||
if (getline(cmdlineFile, cmdline)) {
|
if (String cmdline; getline(cmdlineFile, cmdline)) {
|
||||||
std::ranges::replace(cmdline, '\0', ' ');
|
std::ranges::replace(cmdline, '\0', ' ');
|
||||||
return cmdline;
|
return cmdline;
|
||||||
}
|
}
|
||||||
|
@ -96,75 +106,66 @@ namespace {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DetectHyprlandSpecific() -> String {
|
fn DetectHyprlandSpecific() -> Option<String> {
|
||||||
Result<String, EnvError> xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP");
|
if (Result<String, EnvError> xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) {
|
||||||
|
std::ranges::transform(*xdgCurrentDesktop, xdgCurrentDesktop->begin(), tolower);
|
||||||
|
|
||||||
if (xdgCurrentDesktop) {
|
if (xdgCurrentDesktop->contains("hyprland"sv))
|
||||||
std::ranges::transform(*xdgCurrentDesktop, xdgCurrentDesktop->begin(), ::tolower);
|
|
||||||
|
|
||||||
if (xdgCurrentDesktop->contains("hyprland"))
|
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetEnv("HYPRLAND_INSTANCE_SIGNATURE"))
|
if (GetEnv("HYPRLAND_INSTANCE_SIGNATURE"))
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
|
|
||||||
if (fs::exists("/run/user/" + std::to_string(getuid()) + "/hypr"))
|
if (fs::exists(std::format("/run/user/{}/hypr", getuid())))
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
|
|
||||||
return "";
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetWaylandCompositor() -> String {
|
fn GetWaylandCompositor() -> String {
|
||||||
String hypr = DetectHyprlandSpecific();
|
if (const Option<String> hypr = DetectHyprlandSpecific())
|
||||||
|
return *hypr;
|
||||||
|
|
||||||
if (!hypr.empty())
|
const WaylandDisplayGuard display;
|
||||||
return hypr;
|
|
||||||
|
|
||||||
WaylandDisplayGuard display;
|
|
||||||
|
|
||||||
if (!display)
|
if (!display)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
i32 fileDescriptor = display.fd();
|
const i32 fileDescriptor = display.fd();
|
||||||
|
|
||||||
struct ucred cred;
|
ucred cred;
|
||||||
socklen_t len = sizeof(cred);
|
u32 len = sizeof(cred);
|
||||||
|
|
||||||
if (getsockopt(fileDescriptor, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
|
if (getsockopt(fileDescriptor, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
String compositorName;
|
String compositorName;
|
||||||
|
|
||||||
String commPath = "/proc/" + std::to_string(cred.pid) + "/comm";
|
const String commPath = std::format("/proc/{}/comm", cred.pid);
|
||||||
std::ifstream commFile(commPath);
|
if (std::ifstream commFile(commPath); commFile >> compositorName) {
|
||||||
if (commFile >> compositorName) {
|
const std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n');
|
||||||
std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n');
|
|
||||||
compositorName.erase(removedRange.begin(), removedRange.end());
|
compositorName.erase(removedRange.begin(), removedRange.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
String cmdline = ReadProcessCmdline(cred.pid);
|
if (const String cmdline = ReadProcessCmdline(cred.pid); cmdline.contains("hyprland"sv))
|
||||||
if (cmdline.contains("hyprland"))
|
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
|
|
||||||
String exePath = "/proc/" + std::to_string(cred.pid) + "/exe";
|
const String exePath = std::format("/proc/{}/exe", cred.pid);
|
||||||
|
|
||||||
Array<char, PATH_MAX> buf;
|
Array<char, PATH_MAX> buf;
|
||||||
ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1);
|
if (const isize lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1); lenBuf != -1) {
|
||||||
if (lenBuf != -1) {
|
|
||||||
buf.at(static_cast<usize>(lenBuf)) = '\0';
|
buf.at(static_cast<usize>(lenBuf)) = '\0';
|
||||||
String exe(buf.data());
|
if (const String exe(buf.data()); exe.contains("hyprland"sv))
|
||||||
if (exe.contains("hyprland"))
|
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
}
|
||||||
|
|
||||||
return compositorName.contains("hyprland") ? "Hyprland" : compositorName;
|
return compositorName.contains("hyprland"sv) ? "Hyprland" : compositorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DetectFromEnvVars() -> Option<String> {
|
fn DetectFromEnvVars() -> Option<String> {
|
||||||
if (Result<String, EnvError> xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) {
|
if (Result<String, EnvError> xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) {
|
||||||
const size_t colon = xdgCurrentDesktop->find(':');
|
if (const usize colon = xdgCurrentDesktop->find(':'); colon != String::npos)
|
||||||
|
|
||||||
if (colon != String::npos)
|
|
||||||
return xdgCurrentDesktop->substr(0, colon);
|
return xdgCurrentDesktop->substr(0, colon);
|
||||||
|
|
||||||
return *xdgCurrentDesktop;
|
return *xdgCurrentDesktop;
|
||||||
|
@ -205,11 +206,11 @@ namespace {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
String lowercaseStem = entry.path().stem().string();
|
String lowercaseStem = entry.path().stem().string();
|
||||||
std::ranges::transform(lowercaseStem, lowercaseStem.begin(), ::tolower);
|
std::ranges::transform(lowercaseStem, lowercaseStem.begin(), tolower);
|
||||||
|
|
||||||
for (const Pair pattern : DE_PATTERNS)
|
for (const auto [fst, snd] : DE_PATTERNS)
|
||||||
if (pattern.first == lowercaseStem)
|
if (fst == lowercaseStem)
|
||||||
return String(pattern.second);
|
return String(snd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +231,7 @@ namespace {
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
std::ifstream cmdline("/proc/self/environ");
|
std::ifstream cmdline("/proc/self/environ");
|
||||||
String envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>());
|
const String envVars((std::istreambuf_iterator(cmdline)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
for (const auto& [process, deName] : processChecks)
|
for (const auto& [process, deName] : processChecks)
|
||||||
if (envVars.contains(process))
|
if (envVars.contains(process))
|
||||||
|
@ -241,10 +242,10 @@ namespace {
|
||||||
|
|
||||||
fn GetMprisPlayers(const SharedPointer<DBus::Connection>& connection) -> Result<Vec<String>, NowPlayingError> {
|
fn GetMprisPlayers(const SharedPointer<DBus::Connection>& connection) -> Result<Vec<String>, NowPlayingError> {
|
||||||
try {
|
try {
|
||||||
SharedPointer<DBus::CallMessage> call =
|
const SharedPointer<DBus::CallMessage> call =
|
||||||
DBus::CallMessage::create("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
|
DBus::CallMessage::create("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
|
||||||
|
|
||||||
SharedPointer<DBus::Message> reply = connection->send_with_reply_blocking(call, 500);
|
const SharedPointer<DBus::Message> reply = connection->send_with_reply_blocking(call, 500);
|
||||||
|
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
ERROR_LOG("DBus timeout or null reply in ListNames");
|
ERROR_LOG("DBus timeout or null reply in ListNames");
|
||||||
|
@ -257,7 +258,7 @@ namespace {
|
||||||
|
|
||||||
Vec<String> mprisPlayers;
|
Vec<String> mprisPlayers;
|
||||||
for (const String& name : allNamesStd)
|
for (const String& name : allNamesStd)
|
||||||
if (StringView(name).contains("org.mpris.MediaPlayer2"))
|
if (StringView(name).contains("org.mpris.MediaPlayer2"sv))
|
||||||
mprisPlayers.emplace_back(name);
|
mprisPlayers.emplace_back(name);
|
||||||
|
|
||||||
return mprisPlayers;
|
return mprisPlayers;
|
||||||
|
@ -269,10 +270,9 @@ namespace {
|
||||||
return Err(e.what());
|
return Err(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetOSVersion() -> Result<String, String> {
|
fn os::GetOSVersion() -> Result<String, String> {
|
||||||
constexpr CStr path = "/etc/os-release";
|
constexpr CStr path = "/etc/os-release";
|
||||||
|
|
||||||
std::ifstream file(path);
|
std::ifstream file(path);
|
||||||
|
@ -298,22 +298,22 @@ fn GetOSVersion() -> Result<String, String> {
|
||||||
return Err(std::format("PRETTY_NAME line not found in {}", path));
|
return Err(std::format("PRETTY_NAME line not found in {}", path));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetMemInfo() -> Result<u64, String> {
|
fn os::GetMemInfo() -> Result<u64, String> {
|
||||||
struct sysinfo info;
|
struct sysinfo info;
|
||||||
|
|
||||||
if (sysinfo(&info) != 0)
|
if (sysinfo(&info) != 0)
|
||||||
return Err(std::format("sysinfo failed: {}", std::error_code(errno, std::generic_category()).message()));
|
return Err(std::format("sysinfo failed: {}", std::error_code(errno, std::generic_category()).message()));
|
||||||
|
|
||||||
return static_cast<u64>(info.totalram * info.mem_unit);
|
return info.totalram * info.mem_unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetNowPlaying() -> Result<String, NowPlayingError> {
|
fn os::GetNowPlaying() -> Result<String, NowPlayingError> {
|
||||||
try {
|
try {
|
||||||
SharedPointer<DBus::Dispatcher> dispatcher = DBus::StandaloneDispatcher::create();
|
const SharedPointer<DBus::Dispatcher> dispatcher = DBus::StandaloneDispatcher::create();
|
||||||
if (!dispatcher)
|
if (!dispatcher)
|
||||||
return Err("Failed to create DBus dispatcher");
|
return Err("Failed to create DBus dispatcher");
|
||||||
|
|
||||||
SharedPointer<DBus::Connection> connection = dispatcher->create_connection(DBus::BusType::SESSION);
|
const SharedPointer<DBus::Connection> connection = dispatcher->create_connection(DBus::BusType::SESSION);
|
||||||
if (!connection)
|
if (!connection)
|
||||||
return Err("Failed to connect to session bus");
|
return Err("Failed to connect to session bus");
|
||||||
|
|
||||||
|
@ -326,14 +326,14 @@ fn GetNowPlaying() -> Result<String, NowPlayingError> {
|
||||||
if (mprisPlayers.empty())
|
if (mprisPlayers.empty())
|
||||||
return Err(NowPlayingCode::NoPlayers);
|
return Err(NowPlayingCode::NoPlayers);
|
||||||
|
|
||||||
String activePlayer = mprisPlayers.front();
|
const String activePlayer = mprisPlayers.front();
|
||||||
|
|
||||||
SharedPointer<DBus::CallMessage> metadataCall =
|
const SharedPointer<DBus::CallMessage> metadataCall =
|
||||||
DBus::CallMessage::create(activePlayer, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get");
|
DBus::CallMessage::create(activePlayer, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get");
|
||||||
|
|
||||||
(*metadataCall) << "org.mpris.MediaPlayer2.Player" << "Metadata";
|
*metadataCall << "org.mpris.MediaPlayer2.Player" << "Metadata";
|
||||||
|
|
||||||
SharedPointer<DBus::Message> metadataReply = connection->send_with_reply_blocking(metadataCall, 5000);
|
const SharedPointer<DBus::Message> metadataReply = connection->send_with_reply_blocking(metadataCall, 5000);
|
||||||
|
|
||||||
String title;
|
String title;
|
||||||
String artist;
|
String artist;
|
||||||
|
@ -347,12 +347,11 @@ fn GetNowPlaying() -> Result<String, NowPlayingError> {
|
||||||
if (metadataVariant.type() == DBus::DataType::ARRAY) {
|
if (metadataVariant.type() == DBus::DataType::ARRAY) {
|
||||||
Map<String, DBus::Variant> metadata = metadataVariant.to_map<String, DBus::Variant>();
|
Map<String, DBus::Variant> metadata = metadataVariant.to_map<String, DBus::Variant>();
|
||||||
|
|
||||||
auto titleIter = metadata.find("xesam:title");
|
if (auto titleIter = metadata.find("xesam:title");
|
||||||
if (titleIter != metadata.end() && titleIter->second.type() == DBus::DataType::STRING)
|
titleIter != metadata.end() && titleIter->second.type() == DBus::DataType::STRING)
|
||||||
title = titleIter->second.to_string();
|
title = titleIter->second.to_string();
|
||||||
|
|
||||||
auto artistIter = metadata.find("xesam:artist");
|
if (auto artistIter = metadata.find("xesam:artist"); artistIter != metadata.end()) {
|
||||||
if (artistIter != metadata.end()) {
|
|
||||||
if (artistIter->second.type() == DBus::DataType::ARRAY) {
|
if (artistIter->second.type() == DBus::DataType::ARRAY) {
|
||||||
if (Vec<String> artists = artistIter->second.to_vector<String>(); !artists.empty())
|
if (Vec<String> artists = artistIter->second.to_vector<String>(); !artists.empty())
|
||||||
artist = artists[0];
|
artist = artists[0];
|
||||||
|
@ -369,24 +368,24 @@ fn GetNowPlaying() -> Result<String, NowPlayingError> {
|
||||||
} catch (const Exception& e) { ERROR_LOG("Error processing metadata reply: {}", e.what()); }
|
} catch (const Exception& e) { ERROR_LOG("Error processing metadata reply: {}", e.what()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::format("{}{}{}", artist, (!artist.empty() && !title.empty()) ? " - " : "", title);
|
return std::format("{}{}{}", artist, !artist.empty() && !title.empty() ? " - " : "", title);
|
||||||
} catch (const DBus::Error& e) { return Err(std::format("DBus error: {}", e.what())); } catch (const Exception& e) {
|
} catch (const DBus::Error& e) { return Err(std::format("DBus error: {}", e.what())); } catch (const Exception& e) {
|
||||||
return Err(std::format("General error: {}", e.what()));
|
return Err(std::format("General error: {}", e.what()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetWindowManager() -> String {
|
fn os::GetWindowManager() -> String {
|
||||||
const Result<String, EnvError> waylandDisplay = GetEnv("WAYLAND_DISPLAY");
|
const Result<String, EnvError> waylandDisplay = GetEnv("WAYLAND_DISPLAY");
|
||||||
const Result<String, EnvError> xdgSessionType = GetEnv("XDG_SESSION_TYPE");
|
|
||||||
|
|
||||||
if (waylandDisplay || (xdgSessionType && xdgSessionType->contains("wayland"))) {
|
if (const Result<String, EnvError> xdgSessionType = GetEnv("XDG_SESSION_TYPE");
|
||||||
|
waylandDisplay || (xdgSessionType && xdgSessionType->contains("wayland"sv))) {
|
||||||
String compositor = GetWaylandCompositor();
|
String compositor = GetWaylandCompositor();
|
||||||
if (!compositor.empty())
|
if (!compositor.empty())
|
||||||
return compositor;
|
return compositor;
|
||||||
|
|
||||||
if (const Result<String, EnvError> xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) {
|
if (const Result<String, EnvError> xdgCurrentDesktop = GetEnv("XDG_CURRENT_DESKTOP")) {
|
||||||
std::ranges::transform(compositor, compositor.begin(), ::tolower);
|
std::ranges::transform(compositor, compositor.begin(), tolower);
|
||||||
if (xdgCurrentDesktop->contains("hyprland"))
|
if (xdgCurrentDesktop->contains("hyprland"sv))
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,7 +396,7 @@ fn GetWindowManager() -> String {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetDesktopEnvironment() -> Option<String> {
|
fn os::GetDesktopEnvironment() -> Option<String> {
|
||||||
if (Option<String> desktopEnvironment = DetectFromEnvVars())
|
if (Option<String> desktopEnvironment = DetectFromEnvVars())
|
||||||
return desktopEnvironment;
|
return desktopEnvironment;
|
||||||
|
|
||||||
|
@ -407,19 +406,21 @@ fn GetDesktopEnvironment() -> Option<String> {
|
||||||
return DetectFromProcesses();
|
return DetectFromProcesses();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetShell() -> String {
|
fn os::GetShell() -> String {
|
||||||
const Vec<Pair<String, String>> shellMap {
|
if (const Result<String, EnvError> shellPath = GetEnv("SHELL")) {
|
||||||
|
// clang-format off
|
||||||
|
constexpr Array<Pair<StringView, StringView>, 5> shellMap {{
|
||||||
{ "bash", "Bash" },
|
{ "bash", "Bash" },
|
||||||
{ "zsh", "Zsh" },
|
{ "zsh", "Zsh" },
|
||||||
{ "fish", "Fish" },
|
{ "fish", "Fish" },
|
||||||
{ "nu", "Nushell" },
|
{ "nu", "Nushell" },
|
||||||
{ "sh", "SH" }, // sh last because other shells contain "sh"
|
{ "sh", "SH" }, // sh last because other shells contain "sh"
|
||||||
};
|
}};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
if (const Result<String, EnvError> shellPath = GetEnv("SHELL")) {
|
for (const auto& [exe, name] : shellMap)
|
||||||
for (const auto& shellPair : shellMap)
|
if (shellPath->contains(exe))
|
||||||
if (shellPath->contains(shellPair.first))
|
return String(name);
|
||||||
return shellPair.second;
|
|
||||||
|
|
||||||
return *shellPath; // fallback to the raw shell path
|
return *shellPath; // fallback to the raw shell path
|
||||||
}
|
}
|
||||||
|
@ -427,7 +428,7 @@ fn GetShell() -> String {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetHost() -> String {
|
fn os::GetHost() -> String {
|
||||||
constexpr CStr path = "/sys/class/dmi/id/product_family";
|
constexpr CStr path = "/sys/class/dmi/id/product_family";
|
||||||
|
|
||||||
std::ifstream file(path);
|
std::ifstream file(path);
|
||||||
|
@ -444,21 +445,21 @@ fn GetHost() -> String {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return productFamily.erase(productFamily.find_last_not_of(" \t\n\r") + 1);
|
return String(Trim(productFamily));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetKernelVersion() -> String {
|
fn os::GetKernelVersion() -> String {
|
||||||
struct utsname uts;
|
utsname uts;
|
||||||
|
|
||||||
if (uname(&uts) == -1) {
|
if (uname(&uts) == -1) {
|
||||||
ERROR_LOG("uname() failed: {}", std::error_code(errno, std::generic_category()).message());
|
ERROR_LOG("uname() failed: {}", std::error_code(errno, std::generic_category()).message());
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<CStr>(uts.release);
|
return uts.release;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetDiskUsage() -> Pair<u64, u64> {
|
fn os::GetDiskUsage() -> Pair<u64, u64> {
|
||||||
struct statvfs stat;
|
struct statvfs stat;
|
||||||
|
|
||||||
if (statvfs("/", &stat) == -1) {
|
if (statvfs("/", &stat) == -1) {
|
||||||
|
@ -466,7 +467,9 @@ fn GetDiskUsage() -> Pair<u64, u64> {
|
||||||
return { 0, 0 };
|
return { 0, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable CppRedundantParentheses
|
||||||
return { (stat.f_blocks * stat.f_frsize) - (stat.f_bfree * stat.f_frsize), stat.f_blocks * stat.f_frsize };
|
return { (stat.f_blocks * stat.f_frsize) - (stat.f_bfree * stat.f_frsize), stat.f_blocks * stat.f_frsize };
|
||||||
|
// ReSharper restore CppRedundantParentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
#include <utility> // for std::exchange
|
#include <utility>
|
||||||
|
|
||||||
#include "display_guards.h"
|
#include "display_guards.h"
|
||||||
|
|
||||||
#include "src/util/macros.h"
|
#include "src/util/macros.h"
|
||||||
|
|
||||||
namespace os::linux {
|
namespace os::linux {
|
||||||
DisplayGuard::DisplayGuard(CStr name) : m_Display(XOpenDisplay(name)) {}
|
DisplayGuard::DisplayGuard(const CStr name) : m_Display(XOpenDisplay(name)) {}
|
||||||
|
|
||||||
DisplayGuard::~DisplayGuard() {
|
DisplayGuard::~DisplayGuard() {
|
||||||
if (m_Display)
|
if (m_Display)
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace os::linux {
|
||||||
* Automatically handles resource acquisition and cleanup
|
* Automatically handles resource acquisition and cleanup
|
||||||
*/
|
*/
|
||||||
class DisplayGuard {
|
class DisplayGuard {
|
||||||
private:
|
|
||||||
Display* m_Display;
|
Display* m_Display;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -32,7 +31,7 @@ namespace os::linux {
|
||||||
DisplayGuard(DisplayGuard&& other) noexcept;
|
DisplayGuard(DisplayGuard&& other) noexcept;
|
||||||
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard&;
|
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard&;
|
||||||
|
|
||||||
[[nodiscard]] operator bool() const;
|
[[nodiscard]] explicit operator bool() const;
|
||||||
[[nodiscard]] fn get() const -> Display*;
|
[[nodiscard]] fn get() const -> Display*;
|
||||||
[[nodiscard]] fn defaultRootWindow() const -> Window;
|
[[nodiscard]] fn defaultRootWindow() const -> Window;
|
||||||
};
|
};
|
||||||
|
@ -42,7 +41,6 @@ namespace os::linux {
|
||||||
* Automatically handles resource acquisition and cleanup
|
* Automatically handles resource acquisition and cleanup
|
||||||
*/
|
*/
|
||||||
class WaylandDisplayGuard {
|
class WaylandDisplayGuard {
|
||||||
private:
|
|
||||||
wl_display* m_Display;
|
wl_display* m_Display;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -60,7 +58,7 @@ namespace os::linux {
|
||||||
WaylandDisplayGuard(WaylandDisplayGuard&& other) noexcept;
|
WaylandDisplayGuard(WaylandDisplayGuard&& other) noexcept;
|
||||||
fn operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard&;
|
fn operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard&;
|
||||||
|
|
||||||
[[nodiscard]] 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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,8 +121,7 @@ fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_st
|
||||||
-> void {
|
-> void {
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
const time_point<system_clock, duration<long long, std::ratio<1, 1>>> now =
|
const time_point<system_clock, duration<i64>> now = std::chrono::floor<seconds>(system_clock::now());
|
||||||
std::chrono::floor<std::chrono::seconds>(std::chrono::system_clock::now());
|
|
||||||
|
|
||||||
const auto [color, levelStr] = [&] {
|
const auto [color, levelStr] = [&] {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
|
|
|
@ -259,12 +259,12 @@ using NowPlayingError = std::variant<
|
||||||
|
|
||||||
enum class EnvError : u8 { NotFound, AccessError };
|
enum class EnvError : u8 { NotFound, AccessError };
|
||||||
|
|
||||||
inline auto GetEnv(const String& name) -> Result<String, EnvError> {
|
inline auto GetEnv(CStr name) -> Result<String, EnvError> {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char* rawPtr = nullptr;
|
char* rawPtr = nullptr;
|
||||||
usize bufferSize = 0;
|
usize bufferSize = 0;
|
||||||
|
|
||||||
const i32 err = _dupenv_s(&rawPtr, &bufferSize, name.c_str());
|
const i32 err = _dupenv_s(&rawPtr, &bufferSize, name);
|
||||||
|
|
||||||
const UniquePointer<char, decltype(&free)> ptrManager(rawPtr, free);
|
const UniquePointer<char, decltype(&free)> ptrManager(rawPtr, free);
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ inline auto GetEnv(const String& name) -> Result<String, EnvError> {
|
||||||
|
|
||||||
return ptrManager.get();
|
return ptrManager.get();
|
||||||
#else
|
#else
|
||||||
CStr value = std::getenv(name.c_str());
|
const CStr value = std::getenv(name);
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return Err(EnvError::NotFound);
|
return Err(EnvError::NotFound);
|
||||||
|
|
Loading…
Add table
Reference in a new issue