diff --git a/.clang-tidy b/.clang-tidy index f5d7e7a..d686033 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,7 +4,6 @@ Checks: > -ctad-maybe-unsupported, -abseil-*, -altera-*, - -boost-*, -bugprone-easily-swappable-parameters, -bugprone-implicit-widening-of-multiplication-result, -cert-env33-c, diff --git a/flake.lock b/flake.lock index 5f0eea5..c6a51bc 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,266 @@ { "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, @@ -16,6 +277,69 @@ } }, "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=", @@ -31,10 +355,83 @@ "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", - "treefmt-nix": "treefmt-nix", + "nixvim": "nixvim", + "treefmt-nix": "treefmt-nix_3", "utils": "utils" } }, @@ -53,9 +450,94 @@ "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": "nixpkgs_2" + "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" }, "locked": { "lastModified": 1737483750, @@ -73,7 +555,7 @@ }, "utils": { "inputs": { - "systems": "systems" + "systems": "systems_4" }, "locked": { "lastModified": 1731533236, diff --git a/flake.nix b/flake.nix index bdf5cd1..2dfb3d6 100644 --- a/flake.nix +++ b/flake.nix @@ -2,6 +2,7 @@ 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"; @@ -12,6 +13,7 @@ nixpkgs, treefmt-nix, utils, + nixvim, ... }: utils.lib.eachDefaultSystem ( @@ -76,8 +78,6 @@ 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; [ diff --git a/meson.build b/meson.build index cb8ef88..73791ae 100644 --- a/meson.build +++ b/meson.build @@ -93,8 +93,6 @@ 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 = [] @@ -113,4 +111,4 @@ executable( objc_args: objc_args, link_args: link_args, dependencies: deps, -) +) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 03ec616..4f7265f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,17 +71,15 @@ namespace { fn SystemInfoBox(const Config& config) -> Element { // 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(); - 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() : ""; + const std::string& name = config.general.get().name.get(); + const std::string& date = GetDate(); + 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 std::string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : ""; // Icon constants (using Nerd Font v3) constexpr const char* calendarIcon = "  "; @@ -89,7 +87,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; @@ -167,14 +165,6 @@ 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)); diff --git a/src/os/linux.cpp b/src/os/linux.cpp index f65fe61..ca7683d 100644 --- a/src/os/linux.cpp +++ b/src/os/linux.cpp @@ -1,361 +1,52 @@ #ifdef __linux__ -#include -#include -#include #include -#include #include #include -#include #include -#include #include #include -#include #include "os.h" #include "src/util/macros.h" enum SessionType : u8 { Wayland, X11, TTY, Unknown }; -namespace { - fn MeminfoParse() -> u64 { - constexpr const char* path = "/proc/meminfo"; +fn ParseLineAsNumber(const std::string& input) -> u64 { + usize start = input.find_first_of("0123456789"); - std::ifstream input(path); - if (!input.is_open()) { - ERROR_LOG("Failed to open {}", path); - return 0; - } - - std::string line; - while (std::getline(input, line)) { - if (line.starts_with("MemTotal")) { - const size_t colonPos = line.find(':'); - if (colonPos == std::string::npos) { - ERROR_LOG("Invalid MemTotal line: no colon found"); - return 0; - } - - std::string_view view(line); - view.remove_prefix(colonPos + 1); - - // Trim leading whitespace - const size_t firstNonSpace = view.find_first_not_of(' '); - if (firstNonSpace == std::string_view::npos) { - ERROR_LOG("No number found after colon in MemTotal line"); - return 0; - } - view.remove_prefix(firstNonSpace); - - // Find the end of the numeric part - const size_t end = view.find_first_not_of("0123456789"); - if (end != std::string_view::npos) { - view = view.substr(0, end); - } - - // Get pointers via iterators - const char* startPtr = &*view.begin(); // Safe iterator-to-pointer conversion - const char* endPtr = &*view.end(); // No manual arithmetic - - u64 value = 0; - const auto result = std::from_chars(startPtr, endPtr, value); - if (result.ec != std::errc() || result.ptr != endPtr) { - ERROR_LOG("Failed to parse number in MemTotal line"); - return 0; - } - - return value; - } - } - - ERROR_LOG("MemTotal line not found in {}", path); + if (start == std::string::npos) { + ERROR_LOG("No number found in input"); return 0; } - fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector { - 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 dbusProxy = createProxy(connection, dbusInterface, dbusObjectPath); - - std::vector names; - - dbusProxy->callMethod(dbusMethodListNames).onInterface(dbusInterface).storeResultsTo(names); - - std::vector mprisPlayers; - - for (const std::basic_string& 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& 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(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(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 buf; - ssize_t lenBuf = readlink(exePath.c_str(), buf.data(), buf.size() - 1); - if (lenBuf != -1) { - buf.at(static_cast(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); - } - - // Helper functions - fn ToLowercase(std::string str) -> std::string { - std::ranges::transform(str, str.begin(), ::tolower); - return str; - } - - fn ContainsAny(std::string_view haystack, const std::vector& needles) -> bool { - return std::ranges::any_of(needles, [&](auto& n) { return haystack.find(n) != std::string_view::npos; }); - } - - fn DetectFromEnvVars() -> std::string { - // Check XDG_CURRENT_DESKTOP - if (const char* xdgDe = std::getenv("XDG_CURRENT_DESKTOP")) { - std::string_view sview(xdgDe); - if (!sview.empty()) { - std::string deStr(sview); - if (size_t colon = deStr.find(':'); colon != std::string::npos) - deStr.erase(colon); - if (!deStr.empty()) - return deStr; - } - } - - // Check DESKTOP_SESSION - if (const char* desktopSession = std::getenv("DESKTOP_SESSION")) { - std::string_view sview(desktopSession); - if (!sview.empty()) - return std::string(sview); - } - - return ""; - } - - fn DetectFromSessionFiles() -> std::string { - namespace fs = std::filesystem; - const std::vector sessionPaths = { "/usr/share/xsessions", "/usr/share/wayland-sessions" }; - - const std::vector>> 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(); - auto lowerFilename = ToLowercase(filename); - - for (const auto& [deName, patterns] : dePatterns) { - if (ContainsAny(lowerFilename, patterns)) - return deName; - } - } - } - return ""; - } - - fn DetectFromProcesses() -> std::string { - const std::vector> 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(cmdline)), std::istreambuf_iterator()); - - for (const auto& [process, deName] : processChecks) { - if (envVars.find(process) != std::string::npos) - return deName; - } - - return "Unknown"; - } + usize end = input.find_first_not_of("0123456789", start); + return std::stoull(input.substr(start, end - start)); } +fn MeminfoParse() -> u64 { + constexpr const char* path = "/proc/meminfo"; + + std::ifstream input(path); + + if (!input.is_open()) { + ERROR_LOG("Failed to open {}", path); + return 0; + } + + std::string line; + + while (std::getline(input, line)) + if (line.starts_with("MemTotal")) + return ParseLineAsNumber(line); + + ERROR_LOG("MemTotal line not found in {}", path); + return 0; +} + +fn GetMemInfo() -> u64 { return MeminfoParse() * 1024; } + fn GetOSVersion() -> std::string { constexpr const char* path = "/etc/os-release"; @@ -383,7 +74,32 @@ fn GetOSVersion() -> std::string { return ""; } -fn GetMemInfo() -> u64 { return MeminfoParse() * 1024; } +fn GetMprisPlayers(sdbus::IConnection& connection) -> std::vector { + 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 dbusProxy = createProxy(connection, dbusInterface, dbusObjectPath); + + std::vector names; + + dbusProxy->callMethod(dbusMethodListNames).onInterface(dbusInterface).storeResultsTo(names); + + std::vector mprisPlayers; + + for (const std::basic_string& 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& mprisPlayers) -> string { + if (!mprisPlayers.empty()) + return mprisPlayers.front(); + + return ""; +} fn GetNowPlaying() -> string { try { @@ -431,48 +147,6 @@ 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 { - // Try environment variables first - if (auto desktopEnvironment = DetectFromEnvVars(); !desktopEnvironment.empty()) - return desktopEnvironment; - - // Try session files next - if (auto desktopEnvironment = DetectFromSessionFiles(); !desktopEnvironment.empty()) - return desktopEnvironment; - - // Fallback to process detection - return DetectFromProcesses(); -} - fn GetShell() -> string { const char* shell = std::getenv("SHELL");