i have no idea if this works

This commit is contained in:
Mars 2025-04-21 23:23:37 -04:00
parent 203d56e06b
commit 8293ef42b6
No known key found for this signature in database
GPG key ID: 8EEA6815467B3DB1
6 changed files with 262 additions and 271 deletions

23
_sources/generated.json Normal file
View file

@ -0,0 +1,23 @@
{
"dbus-cxx": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "dbus-cxx",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "dbus-cxx",
"repo": "dbus-cxx",
"rev": "2.5.2",
"sha256": "sha256-if/9XIsf3an5Sij91UIIyNB3vlFAcKrm6YT5Mk7NhB0=",
"sparseCheckout": [],
"type": "github"
},
"version": "2.5.2"
}
}

15
_sources/generated.nix Normal file
View file

@ -0,0 +1,15 @@
# This file was generated by nvfetcher, please do not modify it manually.
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
{
dbus-cxx = {
pname = "dbus-cxx";
version = "2.5.2";
src = fetchFromGitHub {
owner = "dbus-cxx";
repo = "dbus-cxx";
rev = "2.5.2";
fetchSubmodules = false;
sha256 = "sha256-if/9XIsf3an5Sij91UIIyNB3vlFAcKrm6YT5Mk7NhB0=";
};
};
}

View file

@ -28,6 +28,44 @@
)
llvmPackages.stdenv;
sources = import ./_sources/generated.nix {
inherit (pkgs) fetchFromGitHub fetchgit fetchurl dockerTools;
};
dbus-cxx = stdenv.mkDerivation {
inherit (sources.dbus-cxx) pname version src;
nativeBuildInputs = [
pkgs.cmake
pkgs.pkg-config
];
buildInputs = [
pkgs.libsigcxx30
];
preConfigure = ''
set -x # Print commands being run
echo "--- Checking pkg-config paths ---"
echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
echo "--- Searching for sigc++ pc files ---"
find $PKG_CONFIG_PATH -name '*.pc' | grep sigc || echo "No sigc pc file found in PKG_CONFIG_PATH"
echo "--- Running pkg-config check ---"
pkg-config --exists --print-errors 'sigc++-3.0'
if [ $? -ne 0 ]; then
echo "ERROR: pkg-config check for sigc++-3.0 failed!"
# Optionally list all available packages known to pkg-config:
# pkg-config --list-all
fi
echo "--- End Debug ---"
set +x
'';
cmakeFlags = [
"-DENABLE_QT_SUPPORT=OFF"
"-DENABLE_UV_SUPPORT=OFF"
];
};
deps = with pkgs;
[
(glaze.override {enableAvx2 = hostPlatform.isx86;})
@ -35,7 +73,9 @@
++ (with pkgsStatic; [
curl
ftxui
tomlplusplus
(tomlplusplus.overrideAttrs {
doCheck = false;
})
])
++ darwinPkgs
++ linuxPkgs;
@ -44,10 +84,12 @@
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs;
[
libsigcxx30
valgrind
]
++ (with pkgsStatic; [
dbus
dbus-cxx
sqlitecpp
xorg.libX11
wayland

View file

@ -129,7 +129,8 @@ elif host_system == 'linux' or host_system == 'freebsd'
dependency('xau'),
dependency('xdmcp'),
dependency('wayland-client'),
dependency('dbus-1', include_type: 'system'),
dependency('sigc++-3.0', include_type: 'system'),
dependency('dbus-cxx', include_type: 'system'),
]
endif

3
nvfetcher.toml Normal file
View file

@ -0,0 +1,3 @@
[dbus-cxx]
src.github = "dbus-cxx/dbus-cxx"
fetch.github = "dbus-cxx/dbus-cxx"

View file

@ -4,12 +4,11 @@
#include <X11/Xlib.h>
#include <algorithm>
#include <cstring>
#include <dbus/dbus.h>
#include <dirent.h>
#include <expected>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <optional>
#include <ranges>
@ -23,6 +22,15 @@
#include "os.h"
#include "src/util/macros.h"
#ifdef Success
#undef Success
#endif
#ifdef None
#undef None
#endif
#include <dbus-cxx.h>
using std::expected;
using std::optional;
@ -51,7 +59,7 @@ namespace {
using std::ranges::subrange;
using std::ranges::transform;
fn GetX11WindowManager() -> string {
fn GetX11WindowManager() -> String {
Display* display = XOpenDisplay(nullptr);
// If XOpenDisplay fails, likely in a TTY
@ -88,7 +96,7 @@ namespace {
&nitems,
&bytesAfter,
&data
) == Success &&
) == 0 &&
data) {
wmWindow = *bit_cast<Window*>(data);
XFree(data);
@ -107,9 +115,9 @@ namespace {
&nitems,
&bytesAfter,
&data
) == Success &&
) == 0 &&
data) {
string name(bit_cast<char*>(data));
String name(bit_cast<char*>(data));
XFree(data);
XCloseDisplay(display);
return name;
@ -121,16 +129,16 @@ namespace {
return "Unknown (X11)";
}
fn TrimHyprlandWrapper(const string& input) -> string {
fn TrimHyprlandWrapper(const String& input) -> String {
if (input.contains("hyprland"))
return "Hyprland";
return input;
}
fn ReadProcessCmdline(int pid) -> string {
string path = "/proc/" + to_string(pid) + "/cmdline";
fn ReadProcessCmdline(int pid) -> String {
String path = "/proc/" + to_string(pid) + "/cmdline";
ifstream cmdlineFile(path);
string cmdline;
String cmdline;
if (getline(cmdlineFile, cmdline)) {
// Replace null bytes with spaces
replace(cmdline, '\0', ' ');
@ -139,7 +147,7 @@ namespace {
return "";
}
fn DetectHyprlandSpecific() -> string {
fn DetectHyprlandSpecific() -> String {
// Check environment variables first
const char* xdgCurrentDesktop = getenv("XDG_CURRENT_DESKTOP");
if (xdgCurrentDesktop && strcasestr(xdgCurrentDesktop, "hyprland"))
@ -156,9 +164,9 @@ namespace {
return "";
}
fn GetWaylandCompositor() -> string {
fn GetWaylandCompositor() -> String {
// First try Hyprland-specific detection
string hypr = DetectHyprlandSpecific();
String hypr = DetectHyprlandSpecific();
if (!hypr.empty())
return hypr;
@ -179,10 +187,10 @@ namespace {
}
// Read both comm and cmdline
string compositorName;
String compositorName;
// 1. Check comm (might be wrapped)
string commPath = "/proc/" + to_string(cred.pid) + "/comm";
String commPath = "/proc/" + to_string(cred.pid) + "/comm";
ifstream commFile(commPath);
if (commFile >> compositorName) {
subrange removedRange = std::ranges::remove(compositorName, '\n');
@ -190,19 +198,19 @@ namespace {
}
// 2. Check cmdline for actual binary reference
string cmdline = ReadProcessCmdline(cred.pid);
String cmdline = ReadProcessCmdline(cred.pid);
if (cmdline.contains("hyprland")) {
wl_display_disconnect(display);
return "Hyprland";
}
// 3. Check exe symlink
string exePath = "/proc/" + to_string(cred.pid) + "/exe";
String exePath = "/proc/" + to_string(cred.pid) + "/exe";
array<char, PATH_MAX> buf;
ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1);
if (lenBuf != -1) {
buf.at(static_cast<usize>(lenBuf)) = '\0';
string exe(buf.data());
String exe(buf.data());
if (exe.contains("hyprland")) {
wl_display_disconnect(display);
return "Hyprland";
@ -215,7 +223,7 @@ namespace {
return TrimHyprlandWrapper(compositorName);
}
fn DetectFromEnvVars() -> optional<string> {
fn DetectFromEnvVars() -> optional<String> {
// Use RAII to guard against concurrent env modifications
static mutex EnvMutex;
lock_guard<mutex> lock(EnvMutex);
@ -224,17 +232,17 @@ namespace {
if (const char* xdgCurrentDesktop = getenv("XDG_CURRENT_DESKTOP")) {
const string_view sview(xdgCurrentDesktop);
const size_t colon = sview.find(':');
return string(sview.substr(0, colon)); // Direct construct from view
return String(sview.substr(0, colon)); // Direct construct from view
}
// DESKTOP_SESSION
if (const char* desktopSession = getenv("DESKTOP_SESSION"))
return string(string_view(desktopSession)); // Avoid intermediate view storage
return String(string_view(desktopSession)); // Avoid intermediate view storage
return nullopt;
}
fn DetectFromSessionFiles() -> optional<string> {
fn DetectFromSessionFiles() -> optional<String> {
static constexpr array<pair<string_view, string_view>, 12> DE_PATTERNS = {
// clang-format off
pair { "Budgie"sv, "budgie"sv },
@ -258,7 +266,7 @@ namespace {
static constexpr array<string_view, 2> SESSION_PATHS = { "/usr/share/xsessions", "/usr/share/wayland-sessions" };
// Single memory reserve for lowercase conversions
string lowercaseStem;
String lowercaseStem;
lowercaseStem.reserve(32);
for (const auto& path : SESSION_PATHS) {
@ -279,14 +287,14 @@ namespace {
);
if (patternIter != DE_PATTERNS.end() && patternIter->first == lowercaseStem)
return string(patternIter->second);
return String(patternIter->second);
}
}
return nullopt;
}
fn DetectFromProcesses() -> optional<string> {
fn DetectFromProcesses() -> optional<String> {
const array processChecks = {
// clang-format off
pair { "plasmashell"sv, "KDE"sv },
@ -300,105 +308,74 @@ namespace {
};
ifstream cmdline("/proc/self/environ");
string envVars((istreambuf_iterator<char>(cmdline)), istreambuf_iterator<char>());
String envVars((istreambuf_iterator<char>(cmdline)), istreambuf_iterator<char>());
for (const auto& [process, deName] : processChecks)
if (envVars.contains(process))
return string(deName);
return String(deName);
return nullopt;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
fn GetMprisPlayers(DBusConnection* connection) -> vector<string> {
vector<string> mprisPlayers;
DBusError err;
dbus_error_init(&err);
fn GetMprisPlayers(const std::shared_ptr<DBus::Connection>& connection) -> expected<vector<String>, NowPlayingError> {
try {
// Create the method call object
std::shared_ptr<DBus::CallMessage> call =
DBus::CallMessage::create("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
// Create a method call to org.freedesktop.DBus.ListNames
DBusMessage* msg = dbus_message_new_method_call(
"org.freedesktop.DBus", // target service
"/org/freedesktop/DBus", // object path
"org.freedesktop.DBus", // interface name
"ListNames" // method name
);
// Send the message synchronously and get the reply
// Timeout parameter might be needed (e.g., 5000 ms)
std::shared_ptr<DBus::Message> reply = connection->send_with_reply_blocking(call, 5000);
if (!msg) {
DEBUG_LOG("Failed to create message for ListNames.");
return mprisPlayers;
// Check if the reply itself is an error type
if (reply) {
ERROR_LOG("DBus timeout or null reply in ListNames");
return unexpected(LinuxError("DBus timeout in ListNames"));
}
// Send the message and block until we get a reply.
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &err);
dbus_message_unref(msg);
vector<String> allNamesStd;
DBus::MessageIterator reader(*reply);
reader >> allNamesStd;
if (dbus_error_is_set(&err)) {
DEBUG_LOG("DBus error in ListNames: {}", err.message);
dbus_error_free(&err);
return mprisPlayers;
}
if (!reply) {
DEBUG_LOG("No reply received for ListNames.");
return mprisPlayers;
}
// The expected reply signature is "as" (an array of strings)
DBusMessageIter iter;
if (!dbus_message_iter_init(reply, &iter)) {
DEBUG_LOG("Reply has no arguments.");
dbus_message_unref(reply);
return mprisPlayers;
}
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
DEBUG_LOG("Reply argument is not an array.");
dbus_message_unref(reply);
return mprisPlayers;
}
// Iterate over the array of strings
DBusMessageIter subIter;
dbus_message_iter_recurse(&iter, &subIter);
while (dbus_message_iter_get_arg_type(&subIter) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&subIter) == DBUS_TYPE_STRING) {
const char* name = nullptr;
dbus_message_iter_get_basic(&subIter, static_cast<void*>(&name));
if (name && std::string_view(name).contains("org.mpris.MediaPlayer2"))
// Filter for MPRIS players
vector<String> mprisPlayers; // Use std::string as String=std::string
for (const auto& name : allNamesStd) {
if (string_view(name).contains("org.mpris.MediaPlayer2")) {
mprisPlayers.emplace_back(name);
}
dbus_message_iter_next(&subIter);
}
dbus_message_unref(reply);
return mprisPlayers;
} catch (const DBus::Error& e) { // Catch specific dbus-cxx exceptions
ERROR_LOG("DBus::Error exception in ListNames: {}", e.what());
return unexpected(LinuxError(e.what()));
} catch (const std::exception& e) { // Catch other potential standard exceptions
ERROR_LOG("Standard exception getting MPRIS players: {}", e.what());
return unexpected(String(e.what()));
}
}
#pragma clang diagnostic pop
fn GetActivePlayer(const vector<string>& mprisPlayers) -> optional<string> {
// --- Logic remains the same ---
fn GetActivePlayer(const vector<String>& mprisPlayers) -> optional<String> {
if (!mprisPlayers.empty())
return mprisPlayers.front();
return nullopt;
}
}
fn GetOSVersion() -> expected<string, string> {
fn GetOSVersion() -> expected<String, String> {
constexpr const char* path = "/etc/os-release";
ifstream file(path);
if (!file.is_open())
return unexpected("Failed to open " + string(path));
return unexpected("Failed to open " + String(path));
string line;
const string prefix = "PRETTY_NAME=";
String line;
const String prefix = "PRETTY_NAME=";
while (getline(file, line))
if (line.starts_with(prefix)) {
string prettyName = line.substr(prefix.size());
String prettyName = line.substr(prefix.size());
if (!prettyName.empty() && prettyName.front() == '"' && prettyName.back() == '"')
return prettyName.substr(1, prettyName.size() - 2);
@ -406,10 +383,10 @@ fn GetOSVersion() -> expected<string, string> {
return prettyName;
}
return unexpected("PRETTY_NAME line not found in " + string(path));
return unexpected("PRETTY_NAME line not found in " + String(path));
}
fn GetMemInfo() -> expected<u64, string> {
fn GetMemInfo() -> expected<u64, String> {
using std::from_chars, std::errc;
constexpr const char* path = "/proc/meminfo";
@ -417,14 +394,14 @@ fn GetMemInfo() -> expected<u64, string> {
ifstream input(path);
if (!input.is_open())
return unexpected("Failed to open " + string(path));
return unexpected("Failed to open " + String(path));
string line;
String line;
while (getline(input, line)) {
if (line.starts_with("MemTotal")) {
const size_t colonPos = line.find(':');
if (colonPos == string::npos)
if (colonPos == String::npos)
return unexpected("Invalid MemTotal line: no colon found");
string_view view(line);
@ -457,196 +434,126 @@ fn GetMemInfo() -> expected<u64, string> {
}
}
return unexpected("MemTotal line not found in " + string(path));
return unexpected("MemTotal line not found in " + String(path));
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
fn GetNowPlaying() -> expected<string, NowPlayingError> {
DBusError err;
dbus_error_init(&err);
// Connect to the session bus
DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
fn GetNowPlaying() -> expected<String, NowPlayingError> {
try {
// 1. Get Dispatcher and Session Bus Connection
std::shared_ptr<DBus::Dispatcher> dispatcher = DBus::StandaloneDispatcher::create();
if (!dispatcher)
return unexpected(LinuxError("Failed to create DBus dispatcher"));
std::shared_ptr<DBus::Connection> connection = dispatcher->create_connection(DBus::BusType::SESSION);
if (!connection)
if (dbus_error_is_set(&err)) {
ERROR_LOG("DBus connection error: {}", err.message);
return unexpected(LinuxError("Failed to connect to session bus"));
NowPlayingError error = LinuxError(err.message);
dbus_error_free(&err);
// 2. Get list of MPRIS players
auto mprisPlayersResult = GetMprisPlayers(connection);
if (!mprisPlayersResult)
return unexpected(mprisPlayersResult.error()); // Forward the error
return unexpected(error);
}
const vector<String>& mprisPlayers = *mprisPlayersResult;
vector<string> mprisPlayers = GetMprisPlayers(connection);
if (mprisPlayers.empty()) {
dbus_connection_unref(connection);
if (mprisPlayers.empty())
return unexpected(NowPlayingError { NowPlayingCode::NoPlayers });
}
optional<string> activePlayer = GetActivePlayer(mprisPlayers);
if (!activePlayer.has_value()) {
dbus_connection_unref(connection);
// 3. Determine active player
optional<String> activePlayerOpt = GetActivePlayer(mprisPlayers);
if (!activePlayerOpt)
return unexpected(NowPlayingError { NowPlayingCode::NoActivePlayer });
// Use std::string for D-Bus service name
const String& activePlayerService = *activePlayerOpt;
// 4. Call Properties.Get for Metadata
const String interfaceNameStd = "org.mpris.MediaPlayer2.Player";
const String propertyNameStd = "Metadata";
// Create call message
auto call = DBus::CallMessage::create(
activePlayerService, // Target service
"/org/mpris/MediaPlayer2", // Object path
"org.freedesktop.DBus.Properties", // Interface
"Get"
); // Method name
(*call) << interfaceNameStd << propertyNameStd;
// Send message and get reply
std::shared_ptr<DBus::Message> replyMsg = connection->send_with_reply_blocking(call, 5000); // Use a timeout
if (!replyMsg) {
ERROR_LOG("DBus timeout or null reply in Properties.Get");
return unexpected(LinuxError("DBus timeout in Properties.Get"));
}
// Prepare a call to the Properties.Get method to fetch "Metadata"
DBusMessage* msg = dbus_message_new_method_call(
activePlayer->c_str(), // target service (active player)
"/org/mpris/MediaPlayer2", // object path
"org.freedesktop.DBus.Properties", // interface
"Get" // method name
);
// 5. Parse the reply
DBus::Variant metadataVariant;
// Create reader/iterator from the message
DBus::MessageIterator reader(*replyMsg); // Use constructor
// *** Correction: Use get<T> on iterator instead of operator>> ***
reader >> metadataVariant;
if (!msg) {
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* error creating message */ });
// Check the variant's signature
if (metadataVariant.to_signature() != "a{sv}") {
return unexpected("Unexpected reply type for Metadata");
}
String artistStd;
String titleStd;
// Get the dictionary using the templated get<T>() method
std::map<String, DBus::Variant> metadataMap;
auto titleIter = metadataMap.find("xesam:title");
if (titleIter != metadataMap.end() && titleIter->second.to_signature() == "s") {
// Use the cast operator on variant to string
titleStd = static_cast<std::string>(titleIter->second);
}
const char* interfaceName = "org.mpris.MediaPlayer2.Player";
const char* propertyName = "Metadata";
if (!dbus_message_append_args(
msg, DBUS_TYPE_STRING, &interfaceName, DBUS_TYPE_STRING, &propertyName, DBUS_TYPE_INVALID
)) {
dbus_message_unref(msg);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* error appending arguments */ });
}
// Call the method and block until reply is received.
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &err);
dbus_message_unref(msg);
if (dbus_error_is_set(&err)) {
ERROR_LOG("DBus error in Properties.Get: {}", err.message);
NowPlayingError error = LinuxError(err.message);
dbus_error_free(&err);
dbus_connection_unref(connection);
return unexpected(error);
}
if (!reply) {
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* no reply error */ });
}
// The reply should contain a variant holding a dictionary ("a{sv}")
DBusMessageIter iter;
if (!dbus_message_iter_init(reply, &iter)) {
dbus_message_unref(reply);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* no arguments in reply */ });
}
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
dbus_message_unref(reply);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* unexpected argument type */ });
}
// Recurse into the variant to get the dictionary
DBusMessageIter variantIter;
dbus_message_iter_recurse(&iter, &variantIter);
if (dbus_message_iter_get_arg_type(&variantIter) != DBUS_TYPE_ARRAY) {
dbus_message_unref(reply);
dbus_connection_unref(connection);
return unexpected(NowPlayingError { /* expected array type */ });
}
string title;
string artist;
DBusMessageIter arrayIter;
dbus_message_iter_recurse(&variantIter, &arrayIter);
// Iterate over each dictionary entry (each entry is of type dict entry)
while (dbus_message_iter_get_arg_type(&arrayIter) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&arrayIter) == DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter dictEntry;
dbus_message_iter_recurse(&arrayIter, &dictEntry);
// Get the key (a string)
const char* key = nullptr;
if (dbus_message_iter_get_arg_type(&dictEntry) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&dictEntry, static_cast<void*>(&key));
// Move to the value (a variant)
dbus_message_iter_next(&dictEntry);
if (dbus_message_iter_get_arg_type(&dictEntry) == DBUS_TYPE_VARIANT) {
DBusMessageIter valueIter;
dbus_message_iter_recurse(&dictEntry, &valueIter);
if (key && std::string_view(key) == "xesam:title") {
if (dbus_message_iter_get_arg_type(&valueIter) == DBUS_TYPE_STRING) {
const char* val = nullptr;
dbus_message_iter_get_basic(&valueIter, static_cast<void*>(&val));
if (val)
title = val;
}
} else if (key && std::string_view(key) == "xesam:artist") {
// Expect an array of strings
if (dbus_message_iter_get_arg_type(&valueIter) == DBUS_TYPE_ARRAY) {
DBusMessageIter subIter;
dbus_message_iter_recurse(&valueIter, &subIter);
if (dbus_message_iter_get_arg_type(&subIter) == DBUS_TYPE_STRING) {
const char* val = nullptr;
dbus_message_iter_get_basic(&subIter, static_cast<void*>(&val));
if (val)
artist = val;
}
}
}
// For line 525-534
auto artistIter = metadataMap.find("xesam:artist");
if (artistIter != metadataMap.end() && artistIter->second.to_signature() == "as") {
// Cast to vector<String>
std::vector<String> artistsStd = static_cast<std::vector<String>>(artistIter->second);
if (!artistsStd.empty()) {
artistStd = artistsStd.front();
}
}
dbus_message_iter_next(&arrayIter);
}
dbus_message_unref(reply);
dbus_connection_unref(connection);
string result;
if (!artist.empty() && !title.empty())
result = artist + " - " + title;
else if (!title.empty())
result = title;
else if (!artist.empty())
result = artist;
else
result = "";
// 6. Construct result string
String result;
if (!artistStd.empty() && !titleStd.empty())
result = artistStd + " - " + titleStd;
else if (!titleStd.empty())
result = titleStd;
else if (!artistStd.empty())
result = artistStd;
return result;
} catch (const DBus::Error& e) { // Catch specific dbus-cxx exceptions
ERROR_LOG("DBus::Error exception in GetNowPlaying: {}", e.what());
return unexpected(LinuxError(e.what()));
} catch (const std::exception& e) { // Catch other potential standard exceptions
ERROR_LOG("Standard exception in GetNowPlaying: {}", e.what());
return unexpected(String(e.what()));
}
}
#pragma clang diagnostic pop
fn GetWindowManager() -> string {
fn GetWindowManager() -> String {
// Check environment variables first
const char* xdgSessionType = getenv("XDG_SESSION_TYPE");
const char* waylandDisplay = getenv("WAYLAND_DISPLAY");
// Prefer Wayland detection if Wayland session
if ((waylandDisplay != nullptr) || (xdgSessionType && string_view(xdgSessionType).contains("wayland"))) {
string compositor = GetWaylandCompositor();
String compositor = GetWaylandCompositor();
if (!compositor.empty())
return compositor;
// Fallback environment check
const char* xdgCurrentDesktop = getenv("XDG_CURRENT_DESKTOP");
if (xdgCurrentDesktop) {
string desktop(xdgCurrentDesktop);
String desktop(xdgCurrentDesktop);
transform(compositor, compositor.begin(), ::tolower);
if (desktop.contains("hyprland"))
return "hyprland";
@ -654,14 +561,14 @@ fn GetWindowManager() -> string {
}
// X11 detection
string x11wm = GetX11WindowManager();
String x11wm = GetX11WindowManager();
if (!x11wm.empty())
return x11wm;
return "Unknown";
}
fn GetDesktopEnvironment() -> optional<string> {
fn GetDesktopEnvironment() -> optional<String> {
// Try environment variables first
if (auto desktopEnvironment = DetectFromEnvVars(); desktopEnvironment.has_value())
return desktopEnvironment;
@ -674,7 +581,7 @@ fn GetDesktopEnvironment() -> optional<string> {
return DetectFromProcesses();
}
fn GetShell() -> string {
fn GetShell() -> String {
const string_view shell = getenv("SHELL");
if (shell.ends_with("bash"))
@ -688,10 +595,10 @@ fn GetShell() -> string {
if (shell.ends_with("sh"))
return "SH";
return !shell.empty() ? string(shell) : "";
return !shell.empty() ? String(shell) : "";
}
fn GetHost() -> string {
fn GetHost() -> String {
constexpr const char* path = "/sys/class/dmi/id/product_family";
ifstream file(path);
@ -700,7 +607,7 @@ fn GetHost() -> string {
return "";
}
string productFamily;
String productFamily;
if (!getline(file, productFamily)) {
ERROR_LOG("Failed to read from {}", path);
return "";
@ -709,7 +616,7 @@ fn GetHost() -> string {
return productFamily;
}
fn GetKernelVersion() -> string {
fn GetKernelVersion() -> String {
struct utsname uts;
if (uname(&uts) == -1) {