scuklle
This commit is contained in:
parent
95374d942d
commit
f9a9491da3
19
flake.nix
19
flake.nix
|
@ -71,14 +71,17 @@
|
||||||
++ linuxPkgs
|
++ linuxPkgs
|
||||||
++ darwinPkgs;
|
++ darwinPkgs;
|
||||||
|
|
||||||
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs; [
|
linuxPkgs =
|
||||||
pkgsStatic.glib
|
nixpkgs.lib.optionals stdenv.isLinux (with pkgs; [
|
||||||
systemdLibs
|
pkgsStatic.glib
|
||||||
sdbus-cpp
|
systemdLibs
|
||||||
valgrind
|
sdbus-cpp
|
||||||
xorg.libX11
|
valgrind
|
||||||
wayland
|
xorg.libX11
|
||||||
]);
|
])
|
||||||
|
++ (with pkgs.pkgsStatic; [
|
||||||
|
wayland
|
||||||
|
]);
|
||||||
|
|
||||||
darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.pkgsStatic.darwin.apple_sdk.frameworks; [
|
darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.pkgsStatic.darwin.apple_sdk.frameworks; [
|
||||||
Foundation
|
Foundation
|
||||||
|
|
|
@ -114,4 +114,4 @@ executable(
|
||||||
objc_args: objc_args,
|
objc_args: objc_args,
|
||||||
link_args: link_args,
|
link_args: link_args,
|
||||||
dependencies: deps,
|
dependencies: deps,
|
||||||
)
|
)
|
|
@ -8,22 +8,24 @@
|
||||||
using rfl::Result;
|
using rfl::Result;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
inline fn GetConfigPath() -> fs::path {
|
namespace {
|
||||||
|
inline fn GetConfigPath() -> fs::path {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const char* localAppData = std::getenv("LOCALAPPDATA");
|
const char* localAppData = std::getenv("LOCALAPPDATA");
|
||||||
|
|
||||||
if (!localAppData)
|
if (!localAppData)
|
||||||
throw std::runtime_error("Environment variable LOCALAPPDATA is not set");
|
throw std::runtime_error("Environment variable LOCALAPPDATA is not set");
|
||||||
|
|
||||||
return fs::path(localAppData);
|
return fs::path(localAppData);
|
||||||
#else
|
#else
|
||||||
const char* home = std::getenv("HOME");
|
const char* home = std::getenv("HOME");
|
||||||
|
|
||||||
if (!home)
|
if (!home)
|
||||||
throw std::runtime_error("Environment variable HOME is not set");
|
throw std::runtime_error("Environment variable HOME is not set");
|
||||||
|
|
||||||
return fs::path(home) / ".config";
|
return fs::path(home) / ".config";
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Config::getInstance() -> Config {
|
fn Config::getInstance() -> Config {
|
||||||
|
|
23
src/main.cpp
23
src/main.cpp
|
@ -8,6 +8,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
#include "ftxui/screen/color.hpp"
|
||||||
#include "os/os.h"
|
#include "os/os.h"
|
||||||
|
|
||||||
constexpr const bool SHOW_ICONS = true;
|
constexpr const bool SHOW_ICONS = true;
|
||||||
|
@ -73,17 +74,17 @@ namespace {
|
||||||
|
|
||||||
fn SystemInfoBox(const Config& config) -> Element {
|
fn SystemInfoBox(const Config& config) -> Element {
|
||||||
// Fetch data
|
// Fetch data
|
||||||
const std::string& name = config.general.get().name.get();
|
const string& name = config.general.get().name.get();
|
||||||
const std::string& date = GetDate();
|
const string& date = GetDate();
|
||||||
const Weather weather = config.weather.get();
|
const Weather weather = config.weather.get();
|
||||||
const std::string& host = GetHost();
|
const string& host = GetHost();
|
||||||
const std::string& kernelVersion = GetKernelVersion();
|
const string& kernelVersion = GetKernelVersion();
|
||||||
const std::string& osVersion = GetOSVersion();
|
const string& osVersion = GetOSVersion();
|
||||||
const u64 memInfo = GetMemInfo();
|
const u64 memInfo = GetMemInfo();
|
||||||
const std::string& desktopEnvironment = GetDesktopEnvironment();
|
const string& desktopEnvironment = GetDesktopEnvironment();
|
||||||
const std::string& windowManager = GetWindowManager();
|
const string& windowManager = GetWindowManager();
|
||||||
const bool nowPlayingEnabled = config.now_playing.get().enabled;
|
const bool nowPlayingEnabled = config.now_playing.get().enabled;
|
||||||
const std::string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : "";
|
const string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : "";
|
||||||
|
|
||||||
const char *calendarIcon = "", *hostIcon = "", *kernelIcon = "", *osIcon = "", *memoryIcon = "", *weatherIcon = "",
|
const char *calendarIcon = "", *hostIcon = "", *kernelIcon = "", *osIcon = "", *memoryIcon = "", *weatherIcon = "",
|
||||||
*musicIcon = "";
|
*musicIcon = "";
|
||||||
|
|
141
src/os/linux.cpp
141
src/os/linux.cpp
|
@ -6,6 +6,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <filesystem>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
@ -19,6 +20,8 @@
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "src/util/macros.h"
|
#include "src/util/macros.h"
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
enum SessionType : u8 { Wayland, X11, TTY, Unknown };
|
enum SessionType : u8 { Wayland, X11, TTY, Unknown };
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -31,7 +34,7 @@ namespace {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
string line;
|
||||||
while (std::getline(input, line)) {
|
while (std::getline(input, line)) {
|
||||||
if (line.starts_with("MemTotal")) {
|
if (line.starts_with("MemTotal")) {
|
||||||
const size_t colonPos = line.find(':');
|
const size_t colonPos = line.find(':');
|
||||||
|
@ -104,17 +107,19 @@ namespace {
|
||||||
|
|
||||||
fn GetX11WindowManager() -> string {
|
fn GetX11WindowManager() -> string {
|
||||||
Display* display = XOpenDisplay(nullptr);
|
Display* display = XOpenDisplay(nullptr);
|
||||||
|
|
||||||
|
// If XOpenDisplay fails, likely in a TTY
|
||||||
if (!display)
|
if (!display)
|
||||||
return "Unknown (X11)";
|
return "";
|
||||||
|
|
||||||
Atom supportingWmCheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
|
Atom supportingWmCheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
|
||||||
Atom wmName = XInternAtom(display, "_NET_WM_NAME", False);
|
Atom wmName = XInternAtom(display, "_NET_WM_NAME", False);
|
||||||
Atom utf8String = XInternAtom(display, "UTF8_STRING", False);
|
Atom utf8String = XInternAtom(display, "UTF8_STRING", False);
|
||||||
|
|
||||||
// ignore unsafe buffer access warning, can't really get around it
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||||
Window root = DefaultRootWindow(display);
|
Window root = DefaultRootWindow(display); // NOLINT
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
Window wmWindow = 0;
|
Window wmWindow = 0;
|
||||||
|
@ -130,7 +135,8 @@ namespace {
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
False,
|
False,
|
||||||
XA_WINDOW,
|
// XA_WINDOW
|
||||||
|
static_cast<Atom>(33),
|
||||||
&actualType,
|
&actualType,
|
||||||
&actualFormat,
|
&actualFormat,
|
||||||
&nitems,
|
&nitems,
|
||||||
|
@ -157,7 +163,7 @@ namespace {
|
||||||
&data
|
&data
|
||||||
) == Success &&
|
) == Success &&
|
||||||
data) {
|
data) {
|
||||||
std::string name(std::bit_cast<char*>(data));
|
string name(std::bit_cast<char*>(data));
|
||||||
XFree(data);
|
XFree(data);
|
||||||
XCloseDisplay(display);
|
XCloseDisplay(display);
|
||||||
return name;
|
return name;
|
||||||
|
@ -166,19 +172,19 @@ namespace {
|
||||||
|
|
||||||
XCloseDisplay(display);
|
XCloseDisplay(display);
|
||||||
|
|
||||||
return "Unknown (X11)";
|
return "Unknown (X11)"; // Changed to empty string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn TrimHyprlandWrapper(const std::string& input) -> std::string {
|
fn TrimHyprlandWrapper(const std::string& input) -> string {
|
||||||
if (input.find("hyprland") != std::string::npos)
|
if (input.find("hyprland") != std::string::npos)
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ReadProcessCmdline(int pid) -> std::string {
|
fn ReadProcessCmdline(int pid) -> string {
|
||||||
std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
|
string path = "/proc/" + std::to_string(pid) + "/cmdline";
|
||||||
std::ifstream cmdlineFile(path);
|
std::ifstream cmdlineFile(path);
|
||||||
std::string cmdline;
|
string cmdline;
|
||||||
if (std::getline(cmdlineFile, cmdline)) {
|
if (std::getline(cmdlineFile, cmdline)) {
|
||||||
// Replace null bytes with spaces
|
// Replace null bytes with spaces
|
||||||
std::ranges::replace(cmdline, '\0', ' ');
|
std::ranges::replace(cmdline, '\0', ' ');
|
||||||
|
@ -187,30 +193,26 @@ namespace {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DetectHyprlandSpecific() -> std::string {
|
fn DetectHyprlandSpecific() -> string {
|
||||||
// Check environment variables first
|
// Check environment variables first
|
||||||
const char* xdgCurrentDesktop = std::getenv("XDG_CURRENT_DESKTOP");
|
const char* xdgCurrentDesktop = std::getenv("XDG_CURRENT_DESKTOP");
|
||||||
if (xdgCurrentDesktop && strcasestr(xdgCurrentDesktop, "hyprland")) {
|
if (xdgCurrentDesktop && strcasestr(xdgCurrentDesktop, "hyprland"))
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
|
||||||
|
|
||||||
// Check for Hyprland's specific environment variable
|
// Check for Hyprland's specific environment variable
|
||||||
if (std::getenv("HYPRLAND_INSTANCE_SIGNATURE")) {
|
if (std::getenv("HYPRLAND_INSTANCE_SIGNATURE"))
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
|
||||||
|
|
||||||
// Check for Hyprland socket
|
// Check for Hyprland socket
|
||||||
std::string socketPath = "/run/user/" + std::to_string(getuid()) + "/hypr";
|
if (fs::exists("/run/user/" + std::to_string(getuid()) + "/hypr"))
|
||||||
if (std::filesystem::exists(socketPath)) {
|
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetWaylandCompositor() -> std::string {
|
fn GetWaylandCompositor() -> string {
|
||||||
// First try Hyprland-specific detection
|
// First try Hyprland-specific detection
|
||||||
std::string hypr = DetectHyprlandSpecific();
|
string hypr = DetectHyprlandSpecific();
|
||||||
if (!hypr.empty())
|
if (!hypr.empty())
|
||||||
return hypr;
|
return hypr;
|
||||||
|
|
||||||
|
@ -229,10 +231,10 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read both comm and cmdline
|
// Read both comm and cmdline
|
||||||
std::string compositorName;
|
string compositorName;
|
||||||
|
|
||||||
// 1. Check comm (might be wrapped)
|
// 1. Check comm (might be wrapped)
|
||||||
std::string commPath = "/proc/" + std::to_string(cred.pid) + "/comm";
|
string commPath = "/proc/" + std::to_string(cred.pid) + "/comm";
|
||||||
std::ifstream commFile(commPath);
|
std::ifstream commFile(commPath);
|
||||||
if (commFile >> compositorName) {
|
if (commFile >> compositorName) {
|
||||||
std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n');
|
std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n');
|
||||||
|
@ -240,19 +242,19 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Check cmdline for actual binary reference
|
// 2. Check cmdline for actual binary reference
|
||||||
std::string cmdline = ReadProcessCmdline(cred.pid);
|
string cmdline = ReadProcessCmdline(cred.pid);
|
||||||
if (cmdline.find("hyprland") != std::string::npos) {
|
if (cmdline.find("hyprland") != std::string::npos) {
|
||||||
wl_display_disconnect(display);
|
wl_display_disconnect(display);
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Check exe symlink
|
// 3. Check exe symlink
|
||||||
std::string exePath = "/proc/" + std::to_string(cred.pid) + "/exe";
|
string exePath = "/proc/" + std::to_string(cred.pid) + "/exe";
|
||||||
std::array<char, PATH_MAX> buf;
|
std::array<char, PATH_MAX> buf;
|
||||||
ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1);
|
ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1);
|
||||||
if (lenBuf != -1) {
|
if (lenBuf != -1) {
|
||||||
buf.at(static_cast<usize>(lenBuf)) = '\0';
|
buf.at(static_cast<usize>(lenBuf)) = '\0';
|
||||||
std::string exe(buf.data());
|
string exe(buf.data());
|
||||||
if (exe.find("hyprland") != std::string::npos) {
|
if (exe.find("hyprland") != std::string::npos) {
|
||||||
wl_display_disconnect(display);
|
wl_display_disconnect(display);
|
||||||
return "Hyprland";
|
return "Hyprland";
|
||||||
|
@ -266,7 +268,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
fn ToLowercase(std::string str) -> std::string {
|
fn ToLowercase(string str) -> string {
|
||||||
std::ranges::transform(str, str.begin(), ::tolower);
|
std::ranges::transform(str, str.begin(), ::tolower);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
@ -275,12 +277,12 @@ namespace {
|
||||||
return std::ranges::any_of(needles, [&](auto& n) { return haystack.find(n) != std::string_view::npos; });
|
return std::ranges::any_of(needles, [&](auto& n) { return haystack.find(n) != std::string_view::npos; });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DetectFromEnvVars() -> std::string {
|
fn DetectFromEnvVars() -> string {
|
||||||
// Check XDG_CURRENT_DESKTOP
|
// Check XDG_CURRENT_DESKTOP
|
||||||
if (const char* xdgDe = std::getenv("XDG_CURRENT_DESKTOP")) {
|
if (const char* xdgDe = std::getenv("XDG_CURRENT_DESKTOP")) {
|
||||||
std::string_view sview(xdgDe);
|
std::string_view sview(xdgDe);
|
||||||
if (!sview.empty()) {
|
if (!sview.empty()) {
|
||||||
std::string deStr(sview);
|
string deStr(sview);
|
||||||
if (size_t colon = deStr.find(':'); colon != std::string::npos)
|
if (size_t colon = deStr.find(':'); colon != std::string::npos)
|
||||||
deStr.erase(colon);
|
deStr.erase(colon);
|
||||||
if (!deStr.empty())
|
if (!deStr.empty())
|
||||||
|
@ -298,8 +300,7 @@ namespace {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DetectFromSessionFiles() -> std::string {
|
fn DetectFromSessionFiles() -> string {
|
||||||
namespace fs = std::filesystem;
|
|
||||||
const std::vector<fs::path> sessionPaths = { "/usr/share/xsessions", "/usr/share/wayland-sessions" };
|
const std::vector<fs::path> sessionPaths = { "/usr/share/xsessions", "/usr/share/wayland-sessions" };
|
||||||
|
|
||||||
const std::vector<std::pair<std::string, std::vector<std::string>>> dePatterns = {
|
const std::vector<std::pair<std::string, std::vector<std::string>>> dePatterns = {
|
||||||
|
@ -321,8 +322,8 @@ namespace {
|
||||||
if (!entry.is_regular_file())
|
if (!entry.is_regular_file())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const std::string filename = entry.path().stem();
|
const string filename = entry.path().stem();
|
||||||
auto lowerFilename = ToLowercase(filename);
|
auto lowerFilename = ToLowercase(filename);
|
||||||
|
|
||||||
for (const auto& [deName, patterns] : dePatterns) {
|
for (const auto& [deName, patterns] : dePatterns) {
|
||||||
if (ContainsAny(lowerFilename, patterns))
|
if (ContainsAny(lowerFilename, patterns))
|
||||||
|
@ -333,7 +334,7 @@ namespace {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DetectFromProcesses() -> std::string {
|
fn DetectFromProcesses() -> string {
|
||||||
const std::vector<std::pair<std::string, std::string>> processChecks = {
|
const std::vector<std::pair<std::string, std::string>> processChecks = {
|
||||||
{ "plasmashell", "KDE" },
|
{ "plasmashell", "KDE" },
|
||||||
{ "gnome-shell", "GNOME" },
|
{ "gnome-shell", "GNOME" },
|
||||||
|
@ -345,7 +346,7 @@ namespace {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ifstream cmdline("/proc/self/environ");
|
std::ifstream cmdline("/proc/self/environ");
|
||||||
std::string envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>());
|
string envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
for (const auto& [process, deName] : processChecks)
|
for (const auto& [process, deName] : processChecks)
|
||||||
if (envVars.find(process) != std::string::npos)
|
if (envVars.find(process) != std::string::npos)
|
||||||
|
@ -354,23 +355,22 @@ namespace {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn CountNix() noexcept -> std::optional<size_t> {
|
fn CountNix() noexcept -> std::optional<usize> {
|
||||||
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
|
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
|
||||||
constexpr std::string_view querySql = "SELECT COUNT(*) FROM ValidPaths WHERE sigs IS NOT NULL;";
|
constexpr std::string_view querySql = "SELECT COUNT(*) FROM ValidPaths WHERE sigs IS NOT NULL;";
|
||||||
|
|
||||||
sqlite3* sqlDB = nullptr;
|
sqlite3* sqlDB = nullptr;
|
||||||
sqlite3_stmt* stmt = nullptr;
|
sqlite3_stmt* stmt = nullptr;
|
||||||
size_t count = 0;
|
usize count = 0;
|
||||||
|
|
||||||
// 1. Direct URI construction without string concatenation
|
// 1. Direct URI construction without string concatenation
|
||||||
const std::string uri =
|
const string uri =
|
||||||
fmt::format("file:{}{}immutable=1", dbPath, (dbPath.find('?') != std::string_view::npos) ? "&" : "?");
|
fmt::format("file:{}{}immutable=1", dbPath, (dbPath.find('?') != std::string_view::npos) ? "&" : "?");
|
||||||
|
|
||||||
// 2. Open database with optimized flags
|
// 2. Open database with optimized flags
|
||||||
if (sqlite3_open_v2(uri.c_str(), &sqlDB, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI | SQLITE_OPEN_NOMUTEX, nullptr) !=
|
if (sqlite3_open_v2(uri.c_str(), &sqlDB, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI | SQLITE_OPEN_NOMUTEX, nullptr) !=
|
||||||
SQLITE_OK) {
|
SQLITE_OK)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Configure database for maximum read performance
|
// 3. Configure database for maximum read performance
|
||||||
sqlite3_exec(sqlDB, "PRAGMA journal_mode=OFF; PRAGMA mmap_size=268435456;", nullptr, nullptr, nullptr);
|
sqlite3_exec(sqlDB, "PRAGMA journal_mode=OFF; PRAGMA mmap_size=268435456;", nullptr, nullptr, nullptr);
|
||||||
|
@ -378,9 +378,9 @@ namespace {
|
||||||
// 4. Single-step prepared statement execution
|
// 4. Single-step prepared statement execution
|
||||||
if (sqlite3_prepare_v3(sqlDB, querySql.data(), querySql.size(), SQLITE_PREPARE_PERSISTENT, &stmt, nullptr) ==
|
if (sqlite3_prepare_v3(sqlDB, querySql.data(), querySql.size(), SQLITE_PREPARE_PERSISTENT, &stmt, nullptr) ==
|
||||||
SQLITE_OK) {
|
SQLITE_OK) {
|
||||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
|
count = static_cast<usize>(sqlite3_column_int64(stmt, 0));
|
||||||
}
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,42 +389,41 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn CountNixWithCache() noexcept -> std::optional<size_t> {
|
fn CountNixWithCache() noexcept -> std::optional<size_t> {
|
||||||
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
|
constexpr const char* dbPath = "/nix/var/nix/db/db.sqlite";
|
||||||
constexpr std::string_view cachePath = "/tmp/nix_pkg_count.cache";
|
constexpr const char* cachePath = "/tmp/nix_pkg_count.cache";
|
||||||
|
|
||||||
// 1. Check cache validity atomically
|
|
||||||
try {
|
try {
|
||||||
const auto dbMtime = std::filesystem::last_write_time(dbPath);
|
using mtime = fs::file_time_type;
|
||||||
const auto cacheMtime = std::filesystem::last_write_time(cachePath);
|
|
||||||
|
|
||||||
if (std::filesystem::exists(cachePath) && dbMtime <= cacheMtime) {
|
const mtime dbMtime = fs::last_write_time(dbPath);
|
||||||
// Read cached value (atomic read)
|
const mtime cacheMtime = fs::last_write_time(cachePath);
|
||||||
std::ifstream cache(cachePath.data(), std::ios::binary);
|
|
||||||
|
if (fs::exists(cachePath) && dbMtime <= cacheMtime) {
|
||||||
|
std::ifstream cache(cachePath, std::ios::binary);
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
cache.read(std::bit_cast<char*>(&count), sizeof(count));
|
cache.read(std::bit_cast<char*>(&count), sizeof(count));
|
||||||
return cache ? std::optional(count) : std::nullopt;
|
return cache ? std::optional(count) : std::nullopt;
|
||||||
}
|
}
|
||||||
} catch (...) {} // Ignore errors, fall through to rebuild cache
|
} catch (const std::exception& e) { DEBUG_LOG("Cache access failed: {}, rebuilding...", e.what()); }
|
||||||
|
|
||||||
// 2. Compute fresh value
|
const std::optional<usize> count = CountNix();
|
||||||
const auto count = CountNix(); // Original optimized function
|
|
||||||
|
|
||||||
// 3. Update cache atomically (write+rename pattern)
|
|
||||||
if (count) {
|
if (count) {
|
||||||
constexpr std::string_view tmpPath = "/tmp/nix_pkg_count.tmp";
|
constexpr const char* tmpPath = "/tmp/nix_pkg_count.tmp";
|
||||||
{
|
|
||||||
std::ofstream tmp(tmpPath.data(), std::ios::binary | std::ios::trunc);
|
|
||||||
tmp.write(std::bit_cast<const char*>(&*count), sizeof(*count));
|
|
||||||
} // RAII close
|
|
||||||
|
|
||||||
std::filesystem::rename(tmpPath, cachePath);
|
{
|
||||||
|
std::ofstream tmp(tmpPath, std::ios::binary | std::ios::trunc);
|
||||||
|
tmp.write(std::bit_cast<const char*>(&*count), sizeof(*count));
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::rename(tmpPath, cachePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetOSVersion() -> std::string {
|
fn GetOSVersion() -> string {
|
||||||
constexpr const char* path = "/etc/os-release";
|
constexpr const char* path = "/etc/os-release";
|
||||||
|
|
||||||
std::ifstream file(path);
|
std::ifstream file(path);
|
||||||
|
@ -482,14 +481,14 @@ fn GetNowPlaying() -> string {
|
||||||
const std::map<std::basic_string<char>, sdbus::Variant>& metadata =
|
const std::map<std::basic_string<char>, sdbus::Variant>& metadata =
|
||||||
metadataVariant.get<std::map<std::string, sdbus::Variant>>();
|
metadataVariant.get<std::map<std::string, sdbus::Variant>>();
|
||||||
|
|
||||||
std::string title;
|
string title;
|
||||||
auto titleIter = metadata.find("xesam:title");
|
auto titleIter = metadata.find("xesam:title");
|
||||||
if (titleIter != metadata.end() && titleIter->second.containsValueOfType<std::string>()) {
|
if (titleIter != metadata.end() && titleIter->second.containsValueOfType<std::string>()) {
|
||||||
title = titleIter->second.get<std::string>();
|
title = titleIter->second.get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string artist;
|
string artist;
|
||||||
auto artistIter = metadata.find("xesam:artist");
|
auto artistIter = metadata.find("xesam:artist");
|
||||||
if (artistIter != metadata.end() && artistIter->second.containsValueOfType<std::vector<std::string>>()) {
|
if (artistIter != metadata.end() && artistIter->second.containsValueOfType<std::vector<std::string>>()) {
|
||||||
auto artists = artistIter->second.get<std::vector<std::string>>();
|
auto artists = artistIter->second.get<std::vector<std::string>>();
|
||||||
if (!artists.empty()) {
|
if (!artists.empty()) {
|
||||||
|
@ -497,7 +496,7 @@ fn GetNowPlaying() -> string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string result;
|
string result;
|
||||||
if (!artist.empty() && !title.empty()) {
|
if (!artist.empty() && !title.empty()) {
|
||||||
result = artist + " - " + title;
|
result = artist + " - " + title;
|
||||||
} else if (!title.empty()) {
|
} else if (!title.empty()) {
|
||||||
|
@ -528,14 +527,14 @@ fn GetWindowManager() -> string {
|
||||||
|
|
||||||
// Prefer Wayland detection if Wayland session
|
// Prefer Wayland detection if Wayland session
|
||||||
if ((waylandDisplay != nullptr) || (xdgSessionType && strstr(xdgSessionType, "wayland"))) {
|
if ((waylandDisplay != nullptr) || (xdgSessionType && strstr(xdgSessionType, "wayland"))) {
|
||||||
std::string compositor = GetWaylandCompositor();
|
string compositor = GetWaylandCompositor();
|
||||||
if (!compositor.empty())
|
if (!compositor.empty())
|
||||||
return compositor;
|
return compositor;
|
||||||
|
|
||||||
// Fallback environment check
|
// Fallback environment check
|
||||||
const char* xdgCurrentDesktop = std::getenv("XDG_CURRENT_DESKTOP");
|
const char* xdgCurrentDesktop = std::getenv("XDG_CURRENT_DESKTOP");
|
||||||
if (xdgCurrentDesktop) {
|
if (xdgCurrentDesktop) {
|
||||||
std::string desktop(xdgCurrentDesktop);
|
string desktop(xdgCurrentDesktop);
|
||||||
std::ranges::transform(compositor, compositor.begin(), ::tolower);
|
std::ranges::transform(compositor, compositor.begin(), ::tolower);
|
||||||
if (desktop.find("hyprland") != std::string::npos)
|
if (desktop.find("hyprland") != std::string::npos)
|
||||||
return "hyprland";
|
return "hyprland";
|
||||||
|
@ -543,7 +542,7 @@ fn GetWindowManager() -> string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// X11 detection
|
// X11 detection
|
||||||
std::string x11wm = GetX11WindowManager();
|
string x11wm = GetX11WindowManager();
|
||||||
if (!x11wm.empty())
|
if (!x11wm.empty())
|
||||||
return x11wm;
|
return x11wm;
|
||||||
|
|
||||||
|
@ -578,7 +577,7 @@ fn GetHost() -> string {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string productFamily;
|
string productFamily;
|
||||||
if (!std::getline(file, productFamily)) {
|
if (!std::getline(file, productFamily)) {
|
||||||
ERROR_LOG("Failed to read from {}", path);
|
ERROR_LOG("Failed to read from {}", path);
|
||||||
return "";
|
return "";
|
||||||
|
|
0
src/os/linux/pkg_count.cpp
Normal file
0
src/os/linux/pkg_count.cpp
Normal file
28
src/os/linux/pkg_count.h
Normal file
28
src/os/linux/pkg_count.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Get package count from dpkg (Debian/Ubuntu)
|
||||||
|
int get_dpkg_package_count();
|
||||||
|
|
||||||
|
// Get package count from RPM (Red Hat/Fedora/CentOS)
|
||||||
|
int get_rpm_package_count();
|
||||||
|
|
||||||
|
// Get package count from pacman (Arch Linux)
|
||||||
|
int get_pacman_package_count();
|
||||||
|
|
||||||
|
// Get package count from Portage (Gentoo)
|
||||||
|
int get_portage_package_count();
|
||||||
|
|
||||||
|
// Get package count from zypper (openSUSE)
|
||||||
|
int get_zypper_package_count();
|
||||||
|
|
||||||
|
// Get package count from flatpak
|
||||||
|
int get_flatpak_package_count();
|
||||||
|
|
||||||
|
// Get package count from snap
|
||||||
|
int get_snap_package_count();
|
||||||
|
|
||||||
|
// Get package count from AppImage
|
||||||
|
int get_appimage_package_count();
|
||||||
|
|
||||||
|
// Get total package count from all available package managers
|
||||||
|
fn GetTotalPackageCount() -> int;
|
|
@ -42,3 +42,8 @@ fn GetHost() -> string;
|
||||||
* @brief Get the kernel version.
|
* @brief Get the kernel version.
|
||||||
*/
|
*/
|
||||||
fn GetKernelVersion() -> string;
|
fn GetKernelVersion() -> string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of installed packages.
|
||||||
|
*/
|
||||||
|
fn GetPackageCount() -> u64;
|
||||||
|
|
|
@ -38,8 +38,8 @@ void LogImpl(LogLevel level, const std::source_location& loc, fmt::format_string
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const std::string filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
|
const string filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
|
||||||
const struct tm time = *std::localtime(&now);
|
const struct tm time = *std::localtime(&now);
|
||||||
|
|
||||||
// Timestamp and level
|
// Timestamp and level
|
||||||
fmt::print(fg(log_colors::timestamp), "[{:%H:%M:%S}] ", time);
|
fmt::print(fg(log_colors::timestamp), "[{:%H:%M:%S}] ", time);
|
||||||
|
@ -60,7 +60,7 @@ void LogImpl(LogLevel level, const std::source_location& loc, fmt::format_string
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wunused-macros"
|
#pragma clang diagnostic ignored "-Wunused-macros"
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define DEBUG_LOG(...) (void)0
|
#define DEBUG_LOG(...) static_cast<void>(0)
|
||||||
#else
|
#else
|
||||||
#define DEBUG_LOG(...) LogImpl(LogLevel::DEBUG, std::source_location::current(), __VA_ARGS__)
|
#define DEBUG_LOG(...) LogImpl(LogLevel::DEBUG, std::source_location::current(), __VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue