rudimentary de/wm detection

This commit is contained in:
Mars 2025-01-30 19:06:46 -05:00
parent c42265e36d
commit 36bf93de41
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
6 changed files with 360 additions and 545 deletions

View file

@ -4,6 +4,7 @@ Checks: >
-ctad-maybe-unsupported,
-abseil-*,
-altera-*,
-boost-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-cert-env33-c,

View file

@ -1,266 +1,5 @@
{
"nodes": {
"codeium": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1733165974,
"narHash": "sha256-ijRHGhvvfp7dfkb2/8iT5i2SsdZJzH/r1uh4GnoDz5Y=",
"owner": "jcdickinson",
"repo": "codeium.nvim",
"rev": "27d2b1ce8c7ba14dbf6e4504bdea8e5548be5476",
"type": "github"
},
"original": {
"owner": "jcdickinson",
"repo": "codeium.nvim",
"type": "github"
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1728330715,
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
"owner": "numtide",
"repo": "devshell",
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-compat": {
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"revCount": 57,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"nixvim",
"nixvim",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
],
"nixpkgs-stable": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1734279981,
"narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1734093295,
"narHash": "sha256-hSwgGpcZtdDsk1dnzA0xj5cNaHgN9A99hRF/mxMtwS4=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "66c5d8b62818ec4c1edb3e941f55ef78df8141a8",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"ixx": {
"inputs": {
"flake-utils": [
"nixvim",
"nixvim",
"nuschtosSearch",
"flake-utils"
],
"nixpkgs": [
"nixvim",
"nixvim",
"nuschtosSearch",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729958008,
"narHash": "sha256-EiOq8jF4Z/zQe0QYVc3+qSKxRK//CFHMB84aYrYGwEs=",
"owner": "NuschtOS",
"repo": "ixx",
"rev": "9fd01aad037f345350eab2cd45e1946cc66da4eb",
"type": "github"
},
"original": {
"owner": "NuschtOS",
"ref": "v0.0.6",
"repo": "ixx",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733570843,
"narHash": "sha256-sQJAxY1TYWD1UyibN/FnN97paTFuwBw3Vp3DNCyKsMk=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "a35b08d09efda83625bef267eb24347b446c80b8",
"type": "github"
},
"original": {
"owner": "lnl7",
"repo": "nix-darwin",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1738012240,
@ -277,69 +16,6 @@
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1702346276,
"narHash": "sha256-eAQgwIWApFQ40ipeOjVSoK4TEHVd6nbSd9fApiHIw5A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cf28ee258fd5f9a52de6b9865cdb93a1f96d09b7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1734474063,
"narHash": "sha256-Yk7+G3aWZpl9dnPBbZievN3htxiONcLXcxwdE9n0mX4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "86dd3715b283806e773b3cde008baf18dd8a5bf8",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1734119587,
"narHash": "sha256-AKU6qqskl0yf2+JdRdD0cfxX4b9x3KKV5RqA6wijmPM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3566ab7246670a43abd2ffa913cc62dad9cdf7d5",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1733097829,
"narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1735554305,
"narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=",
@ -355,83 +31,10 @@
"type": "github"
}
},
"nixvim": {
"inputs": {
"codeium": "codeium",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_3",
"nixvim": "nixvim_2",
"treefmt-nix": "treefmt-nix_2"
},
"locked": {
"lastModified": 1734505002,
"narHash": "sha256-oH0HhXrLmDb4Q+twZOSgduU9ABzdfcEzVaSePHw4lTk=",
"owner": "pupbrained",
"repo": "nvim-config",
"rev": "d4ece42d394b92ec5fbea5e6a80c2a81a2091a63",
"type": "github"
},
"original": {
"owner": "pupbrained",
"repo": "nvim-config",
"type": "github"
}
},
"nixvim_2": {
"inputs": {
"devshell": "devshell",
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"git-hooks": "git-hooks",
"home-manager": "home-manager",
"nix-darwin": "nix-darwin",
"nixpkgs": "nixpkgs_4",
"nuschtosSearch": "nuschtosSearch",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1734472356,
"narHash": "sha256-RIoG3zXarfuHfzM/z/NPjoHHxl3sqYrtEatSLA1/bIk=",
"owner": "nix-community",
"repo": "nixvim",
"rev": "4f1fe403b18c45614d6b81423038a34cff371244",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixvim",
"type": "github"
}
},
"nuschtosSearch": {
"inputs": {
"flake-utils": "flake-utils_3",
"ixx": "ixx",
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733773348,
"narHash": "sha256-Y47y+LesOCkJaLvj+dI/Oa6FAKj/T9sKVKDXLNsViPw=",
"owner": "NuschtOS",
"repo": "search",
"rev": "3051be7f403bff1d1d380e4612f0c70675b44fc9",
"type": "github"
},
"original": {
"owner": "NuschtOS",
"repo": "search",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"nixvim": "nixvim",
"treefmt-nix": "treefmt-nix_3",
"treefmt-nix": "treefmt-nix",
"utils": "utils"
}
},
@ -450,94 +53,9 @@
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_4": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733761991,
"narHash": "sha256-s4DalCDepD22jtKL5Nw6f4LP5UwoMcPzPZgHWjAfqbQ=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "0ce9d149d99bc383d1f2d85f31f6ebd146e46085",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1733761991,
"narHash": "sha256-s4DalCDepD22jtKL5Nw6f4LP5UwoMcPzPZgHWjAfqbQ=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "0ce9d149d99bc383d1f2d85f31f6ebd146e46085",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_3": {
"inputs": {
"nixpkgs": "nixpkgs_6"
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1737483750,
@ -555,7 +73,7 @@
},
"utils": {
"inputs": {
"systems": "systems_4"
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,

View file

@ -2,7 +2,6 @@
description = "C/C++ environment";
inputs = {
nixvim.url = "github:pupbrained/nvim-config";
nixpkgs.url = "github:NixOS/nixpkgs";
treefmt-nix.url = "github:numtide/treefmt-nix";
utils.url = "github:numtide/flake-utils";
@ -13,7 +12,6 @@
nixpkgs,
treefmt-nix,
utils,
nixvim,
...
}:
utils.lib.eachDefaultSystem (
@ -78,6 +76,8 @@
sdbus-cpp
valgrind
linuxKernel.packages.linux_zen.perf.out
xorg.libX11
wayland
]);
darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.pkgsStatic.darwin.apple_sdk.frameworks; [

View file

@ -93,6 +93,8 @@ if host_machine.system() == 'darwin'
deps += dependency('iconv')
elif host_machine.system() == 'linux' or host_machine.system() == 'freebsd'
deps += dependency('sdbus-c++')
deps += dependency('x11')
deps += dependency('wayland-client')
endif
objc_args = []

View file

@ -73,12 +73,14 @@ namespace {
// Fetch data
const std::string& name = config.general.get().name.get();
const std::string& date = GetDate();
const Weather weather = config.weather.get();
const std::string& host = GetHost();
const std::string& kernelVersion = GetKernelVersion();
const std::string& osVersion = GetOSVersion();
u64 memInfo = GetMemInfo();
Weather weather = config.weather.get();
bool nowPlayingEnabled = config.now_playing.get().enabled;
const u64 memInfo = GetMemInfo();
const std::string& desktopEnvironment = GetDesktopEnvironment();
const std::string& windowManager = GetWindowManager();
const bool nowPlayingEnabled = config.now_playing.get().enabled;
const std::string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : "";
// Icon constants (using Nerd Font v3)
@ -87,7 +89,7 @@ namespace {
constexpr const char* kernelIcon = "";
constexpr const char* osIcon = "";
constexpr const char* memoryIcon = "";
constexpr const char* weatherIcon = " 󰖐 ";
constexpr const char* weatherIcon = " ";
constexpr const char* musicIcon = "";
const Color::Palette16 labelColor = Color::Yellow;
const Color::Palette16 valueColor = Color::White;
@ -165,6 +167,14 @@ namespace {
if (memInfo > 0)
content.push_back(createRow(memoryIcon, "RAM", fmt::format("{:.2f}", BytesToGiB { memInfo })));
content.push_back(separator() | color(borderColor));
if (!desktopEnvironment.empty() && desktopEnvironment != windowManager)
content.push_back(createRow(" 󰇄 ", "DE", desktopEnvironment));
if (!windowManager.empty())
content.push_back(createRow("  ", "WM", windowManager));
// Now Playing row
if (nowPlayingEnabled && !nowPlaying.empty()) {
content.push_back(separator() | color(borderColor));
@ -185,6 +195,9 @@ namespace {
fn main() -> i32 {
const Config& config = Config::getInstance();
DEBUG_LOG("Window Manager: {}", GetWindowManager());
DEBUG_LOG("Desktop Environment: {}", GetDesktopEnvironment());
Element document = hbox({ SystemInfoBox(config), filler() });
Screen screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));

View file

@ -1,18 +1,26 @@
#ifdef __linux__
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <algorithm>
#include <cstring>
#include <dirent.h>
#include <fmt/format.h>
#include <fstream>
#include <ranges>
#include <sdbus-c++/sdbus-c++.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <vector>
#include <wayland-client.h>
#include "os.h"
#include "src/util/macros.h"
enum SessionType : u8 { Wayland, X11, TTY, Unknown };
fn ParseLineAsNumber(const std::string& input) -> u64 {
namespace {
fn ParseLineAsNumber(const std::string& input) -> u64 {
usize start = input.find_first_of("0123456789");
if (start == std::string::npos) {
@ -23,9 +31,9 @@ fn ParseLineAsNumber(const std::string& input) -> u64 {
usize end = input.find_first_not_of("0123456789", start);
return std::stoull(input.substr(start, end - start));
}
}
fn MeminfoParse() -> u64 {
fn MeminfoParse() -> u64 {
constexpr const char* path = "/proc/meminfo";
std::ifstream input(path);
@ -43,9 +51,198 @@ fn MeminfoParse() -> u64 {
ERROR_LOG("MemTotal line not found in {}", path);
return 0;
}
}
fn GetMemInfo() -> u64 { return MeminfoParse() * 1024; }
fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector<string> {
const sdbus::ServiceName dbusInterface = sdbus::ServiceName("org.freedesktop.DBus");
const sdbus::ObjectPath dbusObjectPath = sdbus::ObjectPath("/org/freedesktop/DBus");
const char* dbusMethodListNames = "ListNames";
const std::unique_ptr<sdbus::IProxy> dbusProxy = createProxy(connection, dbusInterface, dbusObjectPath);
std::vector<string> names;
dbusProxy->callMethod(dbusMethodListNames).onInterface(dbusInterface).storeResultsTo(names);
std::vector<string> mprisPlayers;
for (const std::basic_string<char>& name : names)
if (const char* mprisInterfaceName = "org.mpris.MediaPlayer2"; name.find(mprisInterfaceName) != std::string::npos)
mprisPlayers.push_back(name);
return mprisPlayers;
}
fn GetActivePlayer(const std::vector<string>& mprisPlayers) -> string {
if (!mprisPlayers.empty())
return mprisPlayers.front();
return "";
}
fn GetX11WindowManager() -> string {
Display* display = XOpenDisplay(nullptr);
if (!display)
return "Unknown (X11)";
Atom supportingWmCheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
Atom wmName = XInternAtom(display, "_NET_WM_NAME", 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 ignored "-Wunsafe-buffer-usage"
Window root = DefaultRootWindow(display);
#pragma clang diagnostic pop
Window wmWindow = 0;
Atom actualType = 0;
int actualFormat = 0;
unsigned long nitems = 0, bytesAfter = 0;
unsigned char* data = nullptr;
if (XGetWindowProperty(
display,
root,
supportingWmCheck,
0,
1,
False,
XA_WINDOW,
&actualType,
&actualFormat,
&nitems,
&bytesAfter,
&data
) == Success &&
data) {
wmWindow = *std::bit_cast<Window*>(data);
XFree(data);
data = nullptr;
if (XGetWindowProperty(
display,
wmWindow,
wmName,
0,
1024,
False,
utf8String,
&actualType,
&actualFormat,
&nitems,
&bytesAfter,
&data
) == Success &&
data) {
std::string name(std::bit_cast<char*>(data));
XFree(data);
XCloseDisplay(display);
return name;
}
}
XCloseDisplay(display);
return "Unknown (X11)";
}
fn TrimHyprlandWrapper(const std::string& input) -> std::string {
if (input.find("hyprland") != std::string::npos)
return "Hyprland";
return input;
}
fn ReadProcessCmdline(int pid) -> std::string {
std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
std::ifstream cmdlineFile(path);
std::string cmdline;
if (std::getline(cmdlineFile, cmdline)) {
// Replace null bytes with spaces
std::ranges::replace(cmdline, '\0', ' ');
return cmdline;
}
return "";
}
fn DetectHyprlandSpecific() -> std::string {
// Check environment variables first
const char* xdgCurrentDesktop = std::getenv("XDG_CURRENT_DESKTOP");
if (xdgCurrentDesktop && strcasestr(xdgCurrentDesktop, "hyprland")) {
return "Hyprland";
}
// Check for Hyprland's specific environment variable
if (std::getenv("HYPRLAND_INSTANCE_SIGNATURE")) {
return "Hyprland";
}
// Check for Hyprland socket
std::string socketPath = "/run/user/" + std::to_string(getuid()) + "/hypr";
if (std::filesystem::exists(socketPath)) {
return "Hyprland";
}
return "";
}
fn GetWaylandCompositor() -> std::string {
// First try Hyprland-specific detection
std::string hypr = DetectHyprlandSpecific();
if (!hypr.empty())
return hypr;
// Then try the standard Wayland detection
wl_display* display = wl_display_connect(nullptr);
if (!display)
return "";
int fileDescriptor = wl_display_get_fd(display);
struct ucred cred;
socklen_t len = sizeof(cred);
if (getsockopt(fileDescriptor, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) {
wl_display_disconnect(display);
return "";
}
// Read both comm and cmdline
std::string compositorName;
// 1. Check comm (might be wrapped)
std::string commPath = "/proc/" + std::to_string(cred.pid) + "/comm";
std::ifstream commFile(commPath);
if (commFile >> compositorName) {
std::ranges::subrange removedRange = std::ranges::remove(compositorName, '\n');
compositorName.erase(removedRange.begin(), compositorName.end());
}
// 2. Check cmdline for actual binary reference
std::string cmdline = ReadProcessCmdline(cred.pid);
if (cmdline.find("hyprland") != std::string::npos) {
wl_display_disconnect(display);
return "Hyprland";
}
// 3. Check exe symlink
std::string exePath = "/proc/" + std::to_string(cred.pid) + "/exe";
std::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';
std::string exe(buf.data());
if (exe.find("hyprland") != std::string::npos) {
wl_display_disconnect(display);
return "Hyprland";
}
}
wl_display_disconnect(display);
// Final cleanup of wrapper names
return TrimHyprlandWrapper(compositorName);
}
}
fn GetOSVersion() -> std::string {
constexpr const char* path = "/etc/os-release";
@ -74,32 +271,7 @@ fn GetOSVersion() -> std::string {
return "";
}
fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector<string> {
const sdbus::ServiceName dbusInterface = sdbus::ServiceName("org.freedesktop.DBus");
const sdbus::ObjectPath dbusObjectPath = sdbus::ObjectPath("/org/freedesktop/DBus");
const char* dbusMethodListNames = "ListNames";
const std::unique_ptr<sdbus::IProxy> dbusProxy = createProxy(connection, dbusInterface, dbusObjectPath);
std::vector<string> names;
dbusProxy->callMethod(dbusMethodListNames).onInterface(dbusInterface).storeResultsTo(names);
std::vector<string> mprisPlayers;
for (const std::basic_string<char>& name : names)
if (const char* mprisInterfaceName = "org.mpris.MediaPlayer2"; name.find(mprisInterfaceName) != std::string::npos)
mprisPlayers.push_back(name);
return mprisPlayers;
}
fn GetActivePlayer(const std::vector<string>& mprisPlayers) -> string {
if (!mprisPlayers.empty())
return mprisPlayers.front();
return "";
}
fn GetMemInfo() -> u64 { return MeminfoParse() * 1024; }
fn GetNowPlaying() -> string {
try {
@ -147,6 +319,115 @@ fn GetNowPlaying() -> string {
return "";
}
fn GetWindowManager() -> string {
// Check environment variables first
const char* xdgSessionType = std::getenv("XDG_SESSION_TYPE");
const char* waylandDisplay = std::getenv("WAYLAND_DISPLAY");
// Prefer Wayland detection if Wayland session
if ((waylandDisplay != nullptr) || (xdgSessionType && strstr(xdgSessionType, "wayland"))) {
std::string compositor = GetWaylandCompositor();
if (!compositor.empty())
return compositor;
// Fallback environment check
const char* xdgCurrentDesktop = std::getenv("XDG_CURRENT_DESKTOP");
if (xdgCurrentDesktop) {
std::string desktop(xdgCurrentDesktop);
std::ranges::transform(compositor, compositor.begin(), ::tolower);
if (desktop.find("hyprland") != std::string::npos)
return "hyprland";
}
}
// X11 detection
std::string x11wm = GetX11WindowManager();
if (!x11wm.empty())
return x11wm;
return "Unknown";
}
fn GetDesktopEnvironment() -> string {
namespace fs = std::filesystem;
// Check standard environment variables first
if (const char* xdgDe = std::getenv("XDG_CURRENT_DESKTOP")) {
if (xdgDe[0] != '\0') {
std::string de(xdgDe);
if (size_t colon = de.find(':'); colon != std::string::npos)
de.erase(colon);
if (!de.empty())
return de;
}
}
if (const char* desktopSession = std::getenv("DESKTOP_SESSION")) {
if (desktopSession[0] != '\0') {
return desktopSession;
}
}
// Check session files in standard locations
const std::vector<fs::path> sessionPaths = { "/usr/share/xsessions", "/usr/share/wayland-sessions" };
// Map of DE identifiers to their session file patterns
const std::vector<std::pair<std::string, std::vector<std::string>>> dePatterns = {
{ "KDE", { "plasma", "plasmax11", "kde" } },
{ "GNOME", { "gnome", "gnome-xorg", "gnome-wayland" } },
{ "XFCE", { "xfce" } },
{ "MATE", { "mate" } },
{ "Cinnamon", { "cinnamon" } },
{ "Budgie", { "budgie" } },
{ "LXQt", { "lxqt" } },
{ "Unity", { "unity" } }
};
for (const auto& sessionPath : sessionPaths) {
if (!fs::exists(sessionPath))
continue;
for (const auto& entry : fs::directory_iterator(sessionPath)) {
if (!entry.is_regular_file())
continue;
const std::string filename = entry.path().stem();
std::string lowercaseFilename;
std::ranges::transform(filename, std::back_inserter(lowercaseFilename), ::tolower);
for (const auto& [deName, patterns] : dePatterns) {
for (const auto& pattern : patterns) {
if (lowercaseFilename.find(pattern) != std::string::npos) {
return deName;
}
}
}
}
}
// Fallback: Check process-based detection
const std::vector<std::pair<std::string, std::string>> processChecks = {
{ "plasmashell", "KDE" },
{ "gnome-shell", "GNOME" },
{ "xfce4-session", "XFCE" },
{ "mate-session", "MATE" },
{ "cinnamon-sessio", "Cinnamon" },
{ "budgie-wm", "Budgie" },
{ "lxqt-session", "LXQt" }
};
std::ifstream cmdline("/proc/self/environ");
std::string envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>());
for (const auto& [process, deName] : processChecks) {
if (envVars.find(process) != std::string::npos) {
return deName;
}
}
return "Unknown";
}
fn GetShell() -> string {
const char* shell = std::getenv("SHELL");