diff --git a/project_binary_fetch/binary_fetch_v1/AsciiArt.cpp b/project_binary_fetch/binary_fetch_v1/AsciiArt.cpp index 69e87db..bc6f140 100644 --- a/project_binary_fetch/binary_fetch_v1/AsciiArt.cpp +++ b/project_binary_fetch/binary_fetch_v1/AsciiArt.cpp @@ -1,6 +1,7 @@ - + #include "AsciiArt.h" -#include "resource.h" // Essential for IDR_DEFAULT_ASCII +#include "DistroDetector.h" +#include "resource.h" #include #include #include @@ -103,7 +104,12 @@ std::string AsciiArt::getUserArtPath() const { #ifdef _WIN32 return "C:\\Users\\Public\\BinaryFetch\\BinaryArt.txt"; #else - return std::string(getenv("HOME")) + "/.config/BinaryFetch/BinaryArt.txt"; + const char* home = getenv("HOME"); + if (!home) { + struct passwd* pw = getpwuid(getuid()); + home = pw ? pw->pw_dir : "/tmp"; + } + return std::string(home) + "/.config/binaryfetch/BinaryArt.txt"; #endif } @@ -118,12 +124,11 @@ bool AsciiArt::ensureDirectoryExists(const std::string& path) const { #endif } -// ========== THE SELF-HEALING ENGINE (Win32 Resource) ========== +// ========== THE SELF-HEALING ENGINE ========== bool AsciiArt::copyDefaultArt(const std::string& destPath) const { if (!ensureDirectoryExists(destPath)) return false; #ifdef _WIN32 - // IDR_DEFAULT_ASCII is 102 in your resource.h 🚀 HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(102), RT_RCDATA); if (!hRes) return false; @@ -137,572 +142,153 @@ bool AsciiArt::copyDefaultArt(const std::string& destPath) const { dest.close(); return true; } -#endif - return false; -} - -bool AsciiArt::loadArtFromPath(const std::string& filepath) { - artLines.clear(); - artWidths.clear(); - std::ifstream file(filepath); - if (!file.is_open()) { - enabled = false; - return false; - } - - std::string line; - maxWidth = 0; - bool isFirstLine = true; - while (std::getline(file, line)) { - if (!line.empty() && line.back() == '\r') line.pop_back(); - if (isFirstLine) { sanitizeLeadingInvisible(line); isFirstLine = false; } - std::string processedLine = processColorCodes(line); - artLines.push_back(processedLine); - size_t vlen = visible_width(processedLine); - artWidths.push_back((int)vlen); - if ((int)vlen > maxWidth) maxWidth = (int)vlen; - } - height = static_cast(artLines.size()); - enabled = !artLines.empty(); - return enabled; -} - -bool AsciiArt::loadFromFile() { - std::string userArtPath = getUserArtPath(); - std::ifstream checkFile(userArtPath); - bool fileExists = checkFile.good(); - checkFile.close(); - - // Self-Heal if the file is missing 🧬 - if (!fileExists) { - if (!copyDefaultArt(userArtPath)) { - return loadArtFromPath("DefaultAsciiArt.txt"); // Final fallback - } - } - return loadArtFromPath(userArtPath); -} - -// ---------------- LivePrinter ---------------- - -LivePrinter::LivePrinter(const AsciiArt& artRef) : art(artRef), index(0) {} - -void LivePrinter::push(const std::string& infoLine) { - printArtAndPad(); - if (!infoLine.empty()) std::cout << infoLine; - std::cout << '\n'; - index++; -} - -void LivePrinter::printArtAndPad() { - int artH = art.getHeight(); - int maxW = art.getMaxWidth(); - int spacing = art.getSpacing(); - - if (index < artH) { - std::cout << art.getLine(index); - int curW = art.getLineWidth(index); - if (curW < maxW) std::cout << std::string(maxW - curW, ' '); - } - else if (maxW > 0) { - std::cout << std::string(maxW, ' '); - } - if (spacing > 0) std::cout << std::string(spacing, ' '); -} - -void LivePrinter::finish() { - while (index < art.getHeight()) { - printArtAndPad(); - std::cout << '\n'; - index++; - } -} -/* -Color Code Feature: -Use $n in BinaryArt.txt to set colors (n = 1-15): -$1 = red $8 = bright_red -$2 = green $9 = bright_green -$3 = yellow $10 = bright_yellow -$4 = blue $11 = bright_blue -$5 = magenta $12 = bright_magenta -$6 = cyan $13 = bright_cyan -$7 = white $14 = bright_white -$15 = reset - -Example: -$1⠀⣄⠀⠀⠏⣤⣤⣀⡀ // This line will be red -⠀⠀⣻⠃⠀⣰⡿⠛⠁ // This line will be white (default) -$2⠀⡠⠋$3⢀⠐⠁⠀ // This line will be green, then yellow - -Old path: C:\Users\{Username}\AppData\Roaming\BinaryFetch\BinaryArt.txt -New path: C:\Users\Default\AppData\Local\BinaryFetch\BinaryArt.txt -*/ - - - - - - -/* -AsciiArt.cpp v1----------------------------------------------------- -#include "AsciiArt.h" -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include // For SHGetFolderPath -#include // For _mkdir -#else -#include -#include -#include -#endif - -// ---------------- Helper functions for AsciiArt ---------------- - -// Remove ANSI color/format sequences (like "\x1b[31m") from a string -std::string stripAnsiSequences(const std::string& s) { - static const std::regex ansi_re("\x1B\\[[0-9;]*[A-Za-z]"); - return std::regex_replace(s, ansi_re, ""); -} - -// Convert UTF-8 string to wide string (wstring) -std::wstring utf8_to_wstring(const std::string& s) { - try { - std::wstring_convert> conv; - return conv.from_bytes(s); - } - catch (...) { - // fallback: naive conversion - std::wstring w; - w.reserve(s.size()); - for (unsigned char c : s) w.push_back(static_cast(c)); - return w; - } -} - -// Return visible width of a wide character (for printing aligned ASCII art) -int char_display_width(wchar_t wc) { -#if !defined(_WIN32) - int w = wcwidth(wc); - return (w < 0) ? 0 : w; -#else - // Basic width heuristics for CJK and fullwidth characters - if (wc == 0) return 0; - if (wc < 0x1100) return 1; - if ((wc >= 0x1100 && wc <= 0x115F) || - (wc >= 0x2E80 && wc <= 0xA4CF) || - (wc >= 0xAC00 && wc <= 0xD7A3) || - (wc >= 0xF900 && wc <= 0xFAFF) || - (wc >= 0xFE10 && wc <= 0xFE19) || - (wc >= 0xFE30 && wc <= 0xFE6F) || - (wc >= 0xFF00 && wc <= 0xFF60) || - (wc >= 0x20000 && wc <= 0x2FFFD) || - (wc >= 0x30000 && wc <= 0x3FFFD)) - return 2; - return 1; -#endif -} - -// Return visible width of UTF-8 string (ignoring ANSI sequences) -size_t visible_width(const std::string& s) { - const std::string cleaned = stripAnsiSequences(s); - const std::wstring w = utf8_to_wstring(cleaned); - size_t width = 0; - for (size_t i = 0; i < w.size(); ++i) width += static_cast(char_display_width(w[i])); - return width; -} - -// ---------------- Sanitize leading invisible characters ---------------- -void sanitizeLeadingInvisible(std::string& s) { - // Remove UTF-8 BOM (EF BB BF) - if (s.size() >= 3 && - (unsigned char)s[0] == 0xEF && - (unsigned char)s[1] == 0xBB && - (unsigned char)s[2] == 0xBF) { - s.erase(0, 3); - } - - // Remove leading zero-width spaces (U+200B -> E2 80 8B) - while (s.size() >= 3 && - (unsigned char)s[0] == 0xE2 && - (unsigned char)s[1] == 0x80 && - (unsigned char)s[2] == 0x8B) { - s.erase(0, 3); - } -} - -// ---------------- AsciiArt class implementation ---------------- - -AsciiArt::AsciiArt() : maxWidth(0), height(0), enabled(true), spacing(2) { -#ifdef _WIN32 - SetConsoleOutputCP(CP_UTF8); // enable UTF-8 output on Windows console -#endif -} - -// Get the path to user's ASCII art file in AppData -std::string AsciiArt::getUserArtPath() const { -#ifdef _WIN32 - char appDataPath[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, appDataPath))) { - std::string fullPath = std::string(appDataPath) + "\\BinaryFetch\\BinaryArt.txt"; - return fullPath; - } - // Fallback if SHGetFolderPath fails - return "BinaryArt.txt"; -#else - // Linux/Mac: use ~/.config/BinaryFetch/BinaryArt.txt - const char* home = getenv("HOME"); - if (!home) { - struct passwd* pw = getpwuid(getuid()); - home = pw->pw_dir; - } - return std::string(home) + "/.config/BinaryFetch/BinaryArt.txt"; -#endif -} - -// Ensure directory exists (create if needed) -bool AsciiArt::ensureDirectoryExists(const std::string& path) const { - // Extract directory from full file path - size_t lastSlash = path.find_last_of("/\\"); - if (lastSlash == std::string::npos) return true; // No directory in path - - std::string directory = path.substr(0, lastSlash); - -#ifdef _WIN32 - // Try to create directory (will fail silently if exists) - int result = _mkdir(directory.c_str()); - // 0 = success, -1 = error (but might already exist) - if (result == 0 || errno == EEXIST) return true; return false; #else - // Linux/Mac - int result = mkdir(directory.c_str(), 0755); - if (result == 0 || errno == EEXIST) return true; - return false; -#endif -} - -// Copy default art to user location -bool AsciiArt::copyDefaultArt(const std::string& destPath) const { - // Try multiple locations for default art file std::vector searchPaths = { - "Default_Ascii_Art.txt", // Current working directory - "DefaultAsciiArt.txt", // Alternative naming (no underscores) - "./Default_Ascii_Art.txt", // Explicit current dir - "./DefaultAsciiArt.txt", // Explicit current dir (no underscores) - "../Default_Ascii_Art.txt", // Parent directory - "../DefaultAsciiArt.txt", // Parent directory (no underscores) - "../../Default_Ascii_Art.txt", // Grandparent directory - "../../DefaultAsciiArt.txt", // Grandparent directory (no underscores) - FOR x64/Debug builds - "../../../Default_Ascii_Art.txt", // Great-grandparent - "../../../DefaultAsciiArt.txt" // Great-grandparent (no underscores) + "DefaultAsciiArt.txt", + "./DefaultAsciiArt.txt", + "../DefaultAsciiArt.txt", + "/usr/share/binaryfetch/BinaryArt.txt", + "/usr/local/share/binaryfetch/BinaryArt.txt", }; -#ifdef _WIN32 - // Get executable directory on Windows - char exePath[MAX_PATH]; - if (GetModuleFileNameA(NULL, exePath, MAX_PATH)) { + char exePath[4096]; + ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); + if (len != -1) { + exePath[len] = '\0'; std::string exeDir = exePath; - size_t lastSlash = exeDir.find_last_of("\\/"); + size_t lastSlash = exeDir.find_last_of('/'); if (lastSlash != std::string::npos) { exeDir = exeDir.substr(0, lastSlash); - searchPaths.push_back(exeDir + "\\Default_Ascii_Art.txt"); - searchPaths.push_back(exeDir + "\\DefaultAsciiArt.txt"); + searchPaths.push_back(exeDir + "/DefaultAsciiArt.txt"); + searchPaths.push_back(exeDir + "/BinaryArt.txt"); } } -#endif - // Try each path std::ifstream src; - std::string foundPath; - - //std::cout << "Searching for default ASCII art file..." << std::endl; for (const auto& path : searchPaths) { - // std::cout << " Trying: " << path << "... "; src.open(path, std::ios::binary); - if (src.is_open()) { - foundPath = path; - // std::cout << "FOUND!" << std::endl; - break; - } - // std::cout << "not found" << std::endl; - } - - if (!src.is_open()) { - // std::cerr << "\nError: Could not find Default_Ascii_Art.txt in any location." << std::endl; - //std::cerr << "Please ensure Default_Ascii_Art.txt is in the same folder as the executable." << std::endl; - return false; + if (src.is_open()) break; } - // std::cout << "Using default art from: " << foundPath << std::endl; - - // Ensure destination directory exists - if (!ensureDirectoryExists(destPath)) { - //std::cerr << "Warning: Could not create directory for " << destPath << std::endl; - return false; - } + if (!src.is_open()) return false; - // Open destination file std::ofstream dest(destPath, std::ios::binary); if (!dest.is_open()) { - //std::cerr << "Warning: Could not create " << destPath << std::endl; + src.close(); return false; } - // Copy content dest << src.rdbuf(); - src.close(); dest.close(); - - // std::cout << "Created ASCII art file at: " << destPath << std::endl; return true; +#endif } -// Internal helper: Load art from specific path bool AsciiArt::loadArtFromPath(const std::string& filepath) { artLines.clear(); artWidths.clear(); - std::ifstream file(filepath); if (!file.is_open()) { enabled = false; - maxWidth = 0; - height = 0; return false; } std::string line; maxWidth = 0; bool isFirstLine = true; - while (std::getline(file, line)) { if (!line.empty() && line.back() == '\r') line.pop_back(); - - // Sanitize only the first line for BOM / zero-width characters - if (isFirstLine) { - sanitizeLeadingInvisible(line); - isFirstLine = false; - } - - artLines.push_back(line); - size_t vlen = visible_width(line); + if (isFirstLine) { sanitizeLeadingInvisible(line); isFirstLine = false; } + std::string processedLine = processColorCodes(line); + artLines.push_back(processedLine); + size_t vlen = visible_width(processedLine); artWidths.push_back((int)vlen); - if (static_cast(vlen) > maxWidth) maxWidth = static_cast(vlen); + if ((int)vlen > maxWidth) maxWidth = (int)vlen; } - - file.close(); - height = static_cast(artLines.size()); enabled = !artLines.empty(); return enabled; } -// Main load function - automatically manages user's art file bool AsciiArt::loadFromFile() { std::string userArtPath = getUserArtPath(); - - // Check if file exists std::ifstream checkFile(userArtPath); bool fileExists = checkFile.good(); checkFile.close(); if (!fileExists) { - // File doesn't exist - copy from default - //std::cout << "ASCII art not found at: " << userArtPath << std::endl; - // std::cout << "Copying from Default_Ascii_Art.txt..." << std::endl; - +#ifndef _WIN32 + Distro distro = DistroDetector::detect(); + std::string distroArt = DistroDetector::getAsciiArt(distro); + if (!distroArt.empty()) { + return loadArtFromString(distroArt); + } +#endif if (!copyDefaultArt(userArtPath)) { - // std::cerr << "Failed to copy default art. Using fallback." << std::endl; - // Try to load from project folder as last resort - return loadArtFromPath("Default_Ascii_Art.txt"); + return loadArtFromPath("DefaultAsciiArt.txt"); } } - - // Now load from user's location - bool success = loadArtFromPath(userArtPath); - - if (success) { - // std::cout << "ASCII art loaded from: " << userArtPath << std::endl; - } - else { - // std::cerr << "Failed to load ASCII art from: " << userArtPath << std::endl; - } - - return success; -} - -// Advanced: Load from custom path (overrides default behavior) -bool AsciiArt::loadFromFile(const std::string& customPath) { - return loadArtFromPath(customPath); -} - -// Check if ASCII art is enabled -bool AsciiArt::isEnabled() const { - return enabled; -} - -// Enable/disable ASCII art display -void AsciiArt::setEnabled(bool enable) { - enabled = enable; + return loadArtFromPath(userArtPath); } -// Clear loaded ASCII art -void AsciiArt::clear() { +bool AsciiArt::loadArtFromString(const std::string& artContent) { artLines.clear(); artWidths.clear(); maxWidth = 0; - height = 0; + + std::istringstream stream(artContent); + std::string line; + bool isFirstLine = true; + + while (std::getline(stream, line)) { + if (!line.empty() && line.back() == '\r') line.pop_back(); + if (isFirstLine) { sanitizeLeadingInvisible(line); isFirstLine = false; } + std::string processedLine = processColorCodes(line); + artLines.push_back(processedLine); + size_t vlen = visible_width(processedLine); + artWidths.push_back((int)vlen); + if ((int)vlen > maxWidth) maxWidth = (int)vlen; + } + + height = static_cast(artLines.size()); + enabled = !artLines.empty(); + return enabled; } -// ---------------- LivePrinter implementation ---------------- +// ---------------- LivePrinter ---------------- LivePrinter::LivePrinter(const AsciiArt& artRef) : art(artRef), index(0) {} -// Print a line with ASCII art padding void LivePrinter::push(const std::string& infoLine) { printArtAndPad(); if (!infoLine.empty()) std::cout << infoLine; std::cout << '\n'; - std::cout.flush(); - ++index; -} - -// Push a blank line -void LivePrinter::pushBlank() { - printArtAndPad(); - std::cout << '\n'; - std::cout.flush(); - ++index; -} - -// Finish printing remaining ASCII art lines -void LivePrinter::finish() { - while (index < art.getHeight()) { - printArtAndPad(); - std::cout << '\n'; - ++index; - } + index++; } -// Print ASCII art line and pad to max width void LivePrinter::printArtAndPad() { int artH = art.getHeight(); int maxW = art.getMaxWidth(); int spacing = art.getSpacing(); if (index < artH) { - const std::string& a = art.getLine(index); - std::cout << a; + std::cout << art.getLine(index); int curW = art.getLineWidth(index); if (curW < maxW) std::cout << std::string(maxW - curW, ' '); } - else { - if (maxW > 0) std::cout << std::string(maxW, ' '); + else if (maxW > 0) { + std::cout << std::string(maxW, ' '); } - if (spacing > 0) std::cout << std::string(spacing, ' '); } -// Push multi-line formatted string to LivePrinter -void pushFormattedLines(LivePrinter& lp, const std::string& s) { - std::istringstream iss(s); - std::string line; - while (std::getline(iss, line)) { - if (!line.empty() && line.back() == '\r') line.pop_back(); - lp.push(line); +void LivePrinter::finish() { + while (index < art.getHeight()) { + printArtAndPad(); + std::cout << '\n'; + index++; } } - -*/ - - -/* ------------------------------------------------- -DOCUMENTATION ------------------------------------------------- -CLASS: AsciiArt -OBJECT: N/A (used in main.cpp as 'art') -DESCRIPTION: Handles ASCII art loading, alignment, and padding with automatic user file management. -FUNCTIONS: - bool loadFromFile() -> bool - Automatically load ASCII art from user's AppData folder. - If file doesn't exist, copies from Default_Ascii_Art.txt and creates it. - Returns true if successful. - - bool loadFromFile(const std::string& customPath) -> bool - Load ASCII art from custom file path (advanced usage). - Returns true if successful. - - bool isEnabled() const -> bool - Check if ASCII art display is enabled. - - void setEnabled(bool enable) -> void - Enable/disable ASCII art display. - - void clear() -> void - Clear loaded ASCII art lines. - - int getMaxWidth() const -> int - Returns max visible width of loaded ASCII art. - - int getHeight() const -> int - Returns number of ASCII art lines. - - const std::string& getLine(int index) const -> const std::string& - Returns ASCII art line at given index. - - int getLineWidth(int index) const -> int - Returns visible width of line at given index. - - int getSpacing() const -> int - Returns spacing between art and info lines. - -PRIVATE HELPERS: - std::string getUserArtPath() const -> std::string - Get the full path to user's ASCII art file in AppData. - - bool ensureDirectoryExists(const std::string& path) const -> bool - Create directory if it doesn't exist. - - bool copyDefaultArt(const std::string& destPath) const -> bool - Copy Default_Ascii_Art.txt to user location. - - bool loadArtFromPath(const std::string& filepath) -> bool - Load art from specific file path. ------------------------------------------------- -CLASS: LivePrinter -OBJECT: N/A (used in main.cpp as 'lp') -DESCRIPTION: Streams system info lines alongside ASCII art. -FUNCTIONS: - LivePrinter(const AsciiArt& artRef) -> constructor - Initialize LivePrinter with a reference to AsciiArt. - void push(const std::string& infoLine) -> void - Print a line with ASCII art padding. - void pushBlank() -> void - Print a blank line with ASCII art padding. - void finish() -> void - Print remaining ASCII art lines if any. ------------------------------------------------- -HELPER FUNCTIONS: - std::string stripAnsiSequences(const std::string& s) -> std::string - Remove ANSI escape sequences from string. - std::wstring utf8_to_wstring(const std::string& s) -> std::wstring - Convert UTF-8 string to wide string. - int char_display_width(wchar_t wc) -> int - Return display width of wide character. - size_t visible_width(const std::string& s) -> size_t - Return visible width of UTF-8 string (ignoring ANSI codes). - void sanitizeLeadingInvisible(std::string& s) -> void - Remove BOM and zero-width spaces from string start. - void pushFormattedLines(LivePrinter& lp, const std::string& s) -> void - Push multi-line formatted string to LivePrinter. ------------------------------------------------- -*/ \ No newline at end of file diff --git a/project_binary_fetch/binary_fetch_v1/AsciiArt.h b/project_binary_fetch/binary_fetch_v1/AsciiArt.h index 0068a8e..18d38e0 100644 --- a/project_binary_fetch/binary_fetch_v1/AsciiArt.h +++ b/project_binary_fetch/binary_fetch_v1/AsciiArt.h @@ -6,7 +6,7 @@ /* --------------------------------------------------------- - AsciiArt Utilities Helper Functions (Declarations) + AsciiArt Utilities � Helper Functions (Declarations) --------------------------------------------------------- These helpers deal with visual correctness when printing @@ -46,7 +46,7 @@ void sanitizeLeadingInvisible(std::string& s); - Reporting how tall and wide the art is - Providing safe access to art lines for real-time display - This class does NOT print anything itself LivePrinter + This class does NOT print anything itself � LivePrinter handles actual on-screen printing. */ class AsciiArt { @@ -94,6 +94,7 @@ class AsciiArt { // Internal helper: Load art from a specific file path bool loadArtFromPath(const std::string& filepath); + bool loadArtFromString(const std::string& artContent); }; @@ -121,7 +122,7 @@ class LivePrinter { // Each call prints the next art line (or blank padding). void push(const std::string& infoLine); - // Same as push("") convenient for spacing + // Same as push("") � convenient for spacing void pushBlank(); // After finishing all info lines, print any remaining diff --git a/project_binary_fetch/binary_fetch_v1/CMakeLists.txt b/project_binary_fetch/binary_fetch_v1/CMakeLists.txt new file mode 100644 index 0000000..cdd0b77 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/CMakeLists.txt @@ -0,0 +1,193 @@ +cmake_minimum_required(VERSION 3.16) +project(BinaryFetch VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Platform detection +if(WIN32) + set(PLATFORM_WINDOWS TRUE) + add_definitions(-DPLATFORM_WINDOWS=1) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(PLATFORM_LINUX TRUE) + add_definitions(-DPLATFORM_LINUX=1) +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(PLATFORM_FREEBSD TRUE) + add_definitions(-DPLATFORM_FREEBSD=1) +else() + message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}") +endif() + +# Static linking options +option(BUILD_STATIC "Build statically linked binary" ON) + +if(BUILD_STATIC) + if(PLATFORM_LINUX OR PLATFORM_FREEBSD) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + elseif(PLATFORM_WINDOWS) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endif() + +# Common source files (platform-agnostic logic) +set(COMMON_SOURCES + main.cpp + AsciiArt.cpp + ConfigReader.cpp + Helpers.cpp + TimeInfo.cpp + personalization_info.cpp + DistroDetector.cpp +) + +# Windows-specific sources +set(WINDOWS_SOURCES + OSInfo.cpp + CPUInfo.cpp + MemoryInfo.cpp + GPUInfo.cpp + StorageInfo.cpp + NetworkInfo.cpp + DisplayInfo.cpp + UserInfo.cpp + SystemInfo.cpp + PerformanceInfo.cpp + ExtraInfo.cpp + DtailedGPUInfo.cpp + DetailedScreen.cpp + ConsoleUtils.cpp + CompactOS.cpp + CompactCPU.cpp + CompactMemory.cpp + CompactGPU.cpp + CompactScreen.cpp + CompactSystem.cpp + CompactUser.cpp + CompactNetwork.cpp + CompactPerformance.cpp + CompactAudio.cpp + compact_disk_info.cpp +) + +# POSIX/Unix sources (Linux + FreeBSD shared) +set(POSIX_SOURCES + platform/posix/UserInfoPosix.cpp + platform/posix/CompactOSPosix.cpp + platform/posix/CompactCPUPosix.cpp + platform/posix/CompactMemoryPosix.cpp + platform/posix/CompactGPUPosix.cpp + platform/posix/CompactScreenPosix.cpp + platform/posix/CompactNetworkPosix.cpp + platform/posix/CompactUserPosix.cpp + platform/posix/CompactPerformancePosix.cpp + platform/posix/CompactSystemPosix.cpp + platform/posix/compact_disk_infoPosix.cpp + platform/posix/CompactAudioPosix.cpp +) + +# Linux-specific sources +set(LINUX_SOURCES + platform/linux/OSInfoLinux.cpp + platform/linux/CPUInfoLinux.cpp + platform/linux/MemoryInfoLinux.cpp + platform/linux/GPUInfoLinux.cpp + platform/linux/StorageInfoLinux.cpp + platform/linux/NetworkInfoLinux.cpp + platform/linux/DisplayInfoLinux.cpp + platform/linux/SystemInfoLinux.cpp + platform/linux/PerformanceInfoLinux.cpp +) + +# FreeBSD-specific sources +set(FREEBSD_SOURCES + platform/freebsd/OSInfoFreeBSD.cpp + platform/freebsd/CPUInfoFreeBSD.cpp + platform/freebsd/MemoryInfoFreeBSD.cpp + platform/freebsd/GPUInfoFreeBSD.cpp + platform/freebsd/StorageInfoFreeBSD.cpp + platform/freebsd/NetworkInfoFreeBSD.cpp + platform/freebsd/DisplayInfoFreeBSD.cpp + platform/freebsd/SystemInfoFreeBSD.cpp + platform/freebsd/PerformanceInfoFreeBSD.cpp + platform/freebsd/AudioInfoFreeBSD.cpp +) + +# POSIX-specific compact sources (replaces Windows versions) +set(POSIX_COMPACT_SOURCES + platform/posix/ExtraInfoPosix.cpp + platform/posix/DetailedGPUInfoPosix.cpp + platform/posix/DetailedScreenPosix.cpp + platform/posix/ConsoleUtilsPosix.cpp +) + +# Windows compact-related sources +set(WINDOWS_COMPACT_SOURCES + ExtraInfo.cpp + DtailedGPUInfo.cpp + DetailedScreen.cpp + ConsoleUtils.cpp +) + +# Select platform sources +if(PLATFORM_WINDOWS) + set(PLATFORM_SOURCES ${WINDOWS_SOURCES} ${WINDOWS_COMPACT_SOURCES}) + set(PLATFORM_LIBS + wbemuuid + pdh + iphlpapi + ws2_32 + wlanapi + winhttp + dxgi + d3d12 + netapi32 + setupapi + cfgmgr32 + Shcore + ole32 + oleaut32 + ) +elseif(PLATFORM_LINUX) + set(PLATFORM_SOURCES ${POSIX_SOURCES} ${LINUX_SOURCES} ${POSIX_COMPACT_SOURCES}) + set(PLATFORM_LIBS pthread) +elseif(PLATFORM_FREEBSD) + set(PLATFORM_SOURCES ${POSIX_SOURCES} ${FREEBSD_SOURCES} ${POSIX_COMPACT_SOURCES}) + set(PLATFORM_LIBS pthread) +endif() + +# Create executable +add_executable(binaryfetch ${COMMON_SOURCES} ${PLATFORM_SOURCES}) + +# Include directories +target_include_directories(binaryfetch PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/nlohmann + ${CMAKE_CURRENT_SOURCE_DIR}/platform +) + +# Link libraries +target_link_libraries(binaryfetch PRIVATE ${PLATFORM_LIBS}) + +# Compiler warnings +if(MSVC) + target_compile_options(binaryfetch PRIVATE /W4) +else() + target_compile_options(binaryfetch PRIVATE -Wall -Wextra -Wpedantic) +endif() + +# Install +install(TARGETS binaryfetch RUNTIME DESTINATION bin) + +# Platform-specific install paths for config +if(PLATFORM_LINUX) + install(FILES Default_BinaryFetch_Config.json DESTINATION share/binaryfetch RENAME BinaryFetch_Config.json) + install(FILES Default_Ascii_Art.txt DESTINATION share/binaryfetch RENAME BinaryArt.txt) +elseif(PLATFORM_FREEBSD) + install(FILES Default_BinaryFetch_Config.json DESTINATION share/binaryfetch RENAME BinaryFetch_Config.json) + install(FILES Default_Ascii_Art.txt DESTINATION share/binaryfetch RENAME BinaryArt.txt) +endif() + +message(STATUS "Building BinaryFetch for: ${CMAKE_SYSTEM_NAME}") +message(STATUS "Static linking: ${BUILD_STATIC}") diff --git a/project_binary_fetch/binary_fetch_v1/CompactOS.h b/project_binary_fetch/binary_fetch_v1/CompactOS.h index 6376da4..27383ad 100644 --- a/project_binary_fetch/binary_fetch_v1/CompactOS.h +++ b/project_binary_fetch/binary_fetch_v1/CompactOS.h @@ -1,8 +1,22 @@ #pragma once #include +#include + +#if defined(_WIN32) || defined(_WIN64) #include #include -using namespace std; +#endif + +#if defined(__linux__) +#include +#endif + +#if defined(__FreeBSD__) +#include +#include +#include +#endif + class CompactOS { public: std::string getOSName(); diff --git a/project_binary_fetch/binary_fetch_v1/CompactPerformance.h b/project_binary_fetch/binary_fetch_v1/CompactPerformance.h index 6b15f3b..f168a02 100644 --- a/project_binary_fetch/binary_fetch_v1/CompactPerformance.h +++ b/project_binary_fetch/binary_fetch_v1/CompactPerformance.h @@ -1,5 +1,8 @@ #pragma once + +#if defined(_WIN32) || defined(_WIN64) #include +#endif class CompactPerformance { public: diff --git a/project_binary_fetch/binary_fetch_v1/CompactScreen.h b/project_binary_fetch/binary_fetch_v1/CompactScreen.h index 645ba76..b90facb 100644 --- a/project_binary_fetch/binary_fetch_v1/CompactScreen.h +++ b/project_binary_fetch/binary_fetch_v1/CompactScreen.h @@ -1,8 +1,11 @@ -#pragma once +#pragma once #include #include + +#if defined(_WIN32) || defined(_WIN64) #include +#endif struct ScreenInfo { std::string name; // Friendly display name (e.g., "ASUS VG27AQ") @@ -41,13 +44,17 @@ class CompactScreen { private: std::vector screens; - // Population methods +#if defined(_WIN32) || defined(_WIN64) + // Windows-specific population methods bool populateFromDXGI(); bool enrichWithNVAPI(); bool enrichWithADL(); - - // Helper to get friendly name from EDID std::string getFriendlyNameFromEDID(const std::wstring& deviceName); +#else + // POSIX population methods + bool populateFromXrandr(); + bool populateFromDRM(); +#endif // Helper to parse EDID for native resolution and name struct EDIDInfo { diff --git a/project_binary_fetch/binary_fetch_v1/Default_BinaryFetch_Config.json b/project_binary_fetch/binary_fetch_v1/Default_BinaryFetch_Config.json index f3ea1fb..d8b8af8 100644 --- a/project_binary_fetch/binary_fetch_v1/Default_BinaryFetch_Config.json +++ b/project_binary_fetch/binary_fetch_v1/Default_BinaryFetch_Config.json @@ -1,72 +1,10 @@ { "header": { - "enabled": true, - "prefix_color": "bright_red", - "title_color": "bright_cyan", - "show_line": true, - "line_color": "green" + "enabled": false }, "compact_time": { - "enabled": true, - "show_emoji": true, - "emoji_color": "yellow", - "time_section": { - "enabled": true, - "show_label": true, - "show_hour": true, - "show_minute": true, - "show_second": true, - "colors": { - "bracket": "cyan", - "label": "red", - "hour": "cyan", - "minute": "cyan", - "second": "cyan", - "sep": "yellow" - } - }, - "date_section": { - "enabled": true, - "show_label": true, - "show_day": true, - "show_month_name": true, - "show_month_num": false, - "show_year": true, - "colors": { - "bracket": "cyan", - "label": "yellow", - "day": "red", - "month_name": "red", - "month_num": "red", - "year": "red", - "sep": "yellow" - } - }, - "week_section": { - "enabled": true, - "show_label": true, - "show_num": true, - "show_day_name": true, - "colors": { - "bracket": "red", - "label": "blue", - "num": "cyan", - "day_name": "cyan", - "sep": "yellow" - } - }, - "leap_section": { - "enabled": false, - "show_label": true, - "show_val": true, - "colors": { - "bracket": "red", - "label": "green", - "val": "bright_magenta" - } - } + "enabled": false }, - "compact_os": { "enabled": true, "show_name": true, @@ -75,16 +13,16 @@ "show_uptime": true, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "OS": "red", - "(": "blue", - ")": "blue", - "OS_:": "blue", - "name_color": "red", + "emoji_color": "cyan", + "OS": "bright_cyan", + "(": "white", + ")": "white", + "OS_:": "white", + "name_color": "bright_white", "build_color": "cyan", - "arch_color": "blue", - "uptime_label_color": "red", - "uptime_value_color": "cyan" + "arch_color": "bright_cyan", + "uptime_label_color": "white", + "uptime_value_color": "bright_cyan" } }, "compact_cpu": { @@ -95,18 +33,18 @@ "show_clock": true, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "CPU": "blue", - "(": "red", - ")": "red", - "CPU_:": "red", - "name_color": "red", - "core_color": "red", - "thread_color": "red", - "text_color": "red", - "separator_color": "red", - "at_symbol_color": "red", - "clock_color": "red" + "emoji_color": "cyan", + "CPU": "bright_cyan", + "(": "white", + ")": "white", + "CPU_:": "white", + "name_color": "bright_white", + "core_color": "bright_cyan", + "thread_color": "bright_cyan", + "text_color": "white", + "separator_color": "white", + "at_symbol_color": "white", + "clock_color": "bright_cyan" } }, "compact_gpu": { @@ -114,47 +52,46 @@ "show_name": true, "show_usage": true, "show_vram": true, - "show_freq": true, + "show_freq": false, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "GPU": "blue", - "(": "yellow", - ")": "yellow", - "GPU_:": "magenta", - "name_color": "red", - "usage_color": "cyan", - "vram_color": "green", - "at_symbol_color": "blue", - "freq_color": "red" + "emoji_color": "cyan", + "GPU": "bright_cyan", + "(": "white", + ")": "white", + "GPU_:": "white", + "name_color": "bright_white", + "usage_color": "bright_cyan", + "vram_color": "bright_cyan", + "at_symbol_color": "white", + "freq_color": "bright_cyan" } }, - "compact_screen": { "enabled": true, "show_header": true, "show_name": true, "show_resolution": true, - "show_scale": true, + "show_scale": false, "show_upscale": false, "show_refresh": true, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "Display": "red", - "Display_:": "red", - "header_text": "red", - "name_color": "blue", - "(": "cyan", - ")": "cyan", - "resolution_color": "red", - "x": "cyan", - "scale_label": "red", - "scale_value": "blue", - "upscale_label": "red", - "upscale_value": "red", - "@": "red", - "refresh_color": "blue" + "emoji_color": "cyan", + "Display": "bright_cyan", + "Display_:": "white", + "header_text": "bright_cyan", + "name_color": "bright_white", + "(": "white", + ")": "white", + "resolution_color": "bright_cyan", + "x": "white", + "scale_label": "white", + "scale_value": "bright_cyan", + "upscale_label": "white", + "upscale_value": "bright_cyan", + "@": "white", + "refresh_color": "bright_cyan" } }, "compact_memory": { @@ -164,73 +101,56 @@ "show_percent": true, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "Memory": "cyan", - "(": "red", - ")": "red", - "Memory_:": "cyan", - "label_color": "cyan", - "total_color": "cyan", - "free_color": "cyan", - "percent_color": "cyan" + "emoji_color": "cyan", + "Memory": "bright_cyan", + "(": "white", + ")": "white", + "Memory_:": "white", + "label_color": "white", + "total_color": "bright_cyan", + "free_color": "bright_cyan", + "percent_color": "bright_cyan" } }, "compact_audio": { "enabled": true, - "show_input": true, + "show_input": false, "show_output": true, "show_audio_input_emoji": true, "show_audio_output_emoji": true, "colors": { - "audio_input_emoji_color": "yellow", - "audio_output_emoji_color": "yellow", - "Audio Input": "red", - "Audio_Input_:": "blue", - "Audio Output": "red", - "Audio_Output_:": "blue", - "(": "cyan", - ")": "cyan", - "->": "cyan", - "device_color": "blue", - "status_color": "blue" + "audio_input_emoji_color": "cyan", + "audio_output_emoji_color": "cyan", + "Audio Input": "bright_cyan", + "Audio_Input_:": "white", + "Audio Output": "bright_cyan", + "Audio_Output_:": "white", + "(": "white", + ")": "white", + "->": "white", + "device_color": "bright_white", + "status_color": "bright_cyan" } }, "compact_performance": { - "enabled": true, - "show_cpu": true, - "show_gpu": true, - "show_ram": true, - "show_disk": true, - "show_emoji": true, - "colors": { - "emoji_color": "yellow", - "Performance": "blue", - "(": "red", - ")": "red", - "Performance_:": "bright_cyan", - "label_color": "bright_cyan", - "cpu_color": "bright_cyan", - "gpu_color": "bright_cyan", - "ram_color": "bright_cyan", - "disk_color": "bright_cyan" - } + "enabled": false }, "compact_user": { "enabled": true, "show_username": true, "show_domain": true, - "show_type": true, + "show_type": false, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "User": "cyan", - "(": "red", - ")": "red", - "User_:": "bright_magenta", - "username_color": "red", - "label_color": "blue", - "domain_color": "blue", - "type_color": "green" + "emoji_color": "cyan", + "User": "bright_cyan", + "(": "white", + ")": "white", + "User_:": "white", + "username_color": "bright_white", + "label_color": "white", + "domain_color": "bright_cyan", + "type_color": "bright_cyan" } }, "compact_network": { @@ -240,54 +160,39 @@ "show_ip": true, "show_emoji": true, "colors": { - "emoji_color": "yellow", - "Network": "cyan", - "(": "red", - ")": "red", - "Network_:": "bright_cyan", - "label_color": "bright_cyan", - "name_color": "bright_cyan", + "emoji_color": "cyan", + "Network": "bright_cyan", + "(": "white", + ")": "white", + "Network_:": "white", + "label_color": "white", + "name_color": "bright_white", "type_color": "bright_cyan", "ip_color": "bright_cyan" } }, "dummy_compact_network": { - "enabled": false, - "show_name": true, - "show_type": true, - "show_ip": true, - "show_emoji": true, - "colors": { - "emoji_color": "yellow", - "Network": "cyan", - "(": "red", - ")": "red", - "Network_:": "bright_cyan", - "label_color": "bright_cyan", - "name_color": "bright_cyan", - "type_color": "bright_cyan", - "ip_color": "bright_cyan" - } + "enabled": false }, "compact_disk": { "enabled": true, "show_usage": true, - "show_capacity": true, + "show_capacity": false, "show_disk_usage_emoji": true, "show_disk_capacity_emoji": true, "colors": { - "disk_usage_emoji_color": "yellow", - "disk_capacity_emoji_color": "yellow", - "Disk Usage": "green", - "Disk_Usage_:": "red", - "Disk Cap": "green", - "Disk_Cap_:": "red", - "(": "red", - ")": "red", - "->": "bright_cyan", - "letter_color": "bright_cyan", + "disk_usage_emoji_color": "cyan", + "disk_capacity_emoji_color": "cyan", + "Disk Usage": "bright_cyan", + "Disk_Usage_:": "white", + "Disk Cap": "bright_cyan", + "Disk_Cap_:": "white", + "(": "white", + ")": "white", + "->": "white", + "letter_color": "bright_white", "percent_color": "bright_cyan", - "separator_color": "bright_cyan", + "separator_color": "white", "capacity_color": "bright_cyan" } }, @@ -301,34 +206,34 @@ "modules": true }, "colors": { - ">>~": "blue", - "header_title": "bright_magenta", - "-------------------------*": "blue", - "label": "magenta", - "total_value": "bright_blue", + ">>~": "cyan", + "header_title": "bright_cyan", + "-------------------------*": "cyan", + "label": "white", + "total_value": "bright_cyan", "free_value": "bright_cyan", - "used_value": "bright_red", - "brackets": "red", + "used_value": "bright_cyan", + "brackets": "white", "~": "cyan", - "module_label": "bright_blue", - " : ": "red", + "module_label": "bright_cyan", + " : ": "white", "capacity": "bright_cyan", - "type": "magenta", - "speed": "bright_blue" + "type": "white", + "speed": "bright_cyan" } }, "detailed_storage": { "enabled": true, "sections": { "storage_summary": true, - "disk_performance": true, + "disk_performance": false, "disk_performance_predicted": false }, "storage_summary": { "header": { "show_header": true, - "line_color": "blue", - "title_color": "bright_yellow" + "line_color": "cyan", + "title_color": "bright_cyan" }, "show_storage_type": true, "show_drive_letter": true, @@ -337,76 +242,55 @@ "show_total_space": true, "show_used_percentage": true, "show_file_system": true, - "show_external_status": true, - "storage_type_color": "cyan", - "drive_letter_color": "bright_blue", - "used_label_color": "red", + "show_external_status": false, + "storage_type_color": "white", + "drive_letter_color": "bright_cyan", + "used_label_color": "white", "used_space_color": "bright_cyan", - "total_space_color": "cyan", - "used_percentage_color": "bright_yellow", - "file_system_color": "red", - "external_text_color": "blue", - "internal_text_color": "bright_cyan", - "[": "red", - "]": "red", - "(": "cyan", - ")": "cyan", - "/": "red", - "-": "cyan", - "used_GIB": "red", - "total_GIB": "blue" + "total_space_color": "bright_cyan", + "used_percentage_color": "bright_cyan", + "file_system_color": "white", + "external_text_color": "white", + "internal_text_color": "white", + "[": "white", + "]": "white", + "(": "white", + ")": "white", + "/": "white", + "-": "white", + "used_GIB": "white", + "total_GIB": "white" }, "disk_performance": { "header": { "show_header": true, - "line_color": "blue", - "title_color": "bright_yellow" + "line_color": "cyan", + "title_color": "bright_cyan" }, "show_drive_letter": true, - "drive_letter_color": "bright_blue", + "drive_letter_color": "bright_cyan", "show_read_speed": true, - "read_label_color": "cyan", - "read_speed_color": "red", + "read_label_color": "white", + "read_speed_color": "bright_cyan", "show_write_speed": true, - "write_label_color": "cyan", - "write_speed_color": "red", - "show_serial_number": true, - "serial_number_color": "cyan", - "show_external_status": true, - "speed_unit_color": "bright_cyan", - "]": "red", - "[": "red", - "(": "cyan", - ")": "cyan", - "/": "red", - "-": "green", - "|": "green" + "write_label_color": "white", + "write_speed_color": "bright_cyan", + "show_serial_number": false, + "serial_number_color": "white", + "show_external_status": false, + "speed_unit_color": "white", + "]": "white", + "[": "white", + "(": "white", + ")": "white", + "/": "white", + "-": "white", + "|": "white" }, "disk_performance_predicted": { "header": { - "show_header": true, - "line_color": "blue", - "title_color": "bright_yellow" - }, - "show_drive_letter": true, - "drive_letter_color": "bright_blue", - "show_read_speed": true, - "read_label_color": "cyan", - "read_speed_color": "red", - "show_write_speed": true, - "write_label_color": "cyan", - "write_speed_color": "red", - "show_serial_number": true, - "serial_number_color": "cyan", - "show_external_status": true, - "speed_unit_color": "bright_cyan", - "]": "red", - "[": "red", - "(": "green", - "/": "red", - "-": "green", - "GIB": "bright_yellow", - "|": "green" + "show_header": false + } } }, "network_info": { @@ -415,65 +299,27 @@ "show_name": true, "show_type": true, "show_local_ip": true, - "show_public_ip": true, - "show_locale": true, - "show_mac": true, - "show_upload": true, - "show_download": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "separator_line": "red", - "header_text_color": "bright_yellow", - "name_label_color": "blue", - "name_value_color": "blue", - "type_label_color": "blue", - "type_value_color": "cyan", - "local_ip_label_color": "blue", - "local_ip_color": "red", - "public_ip_label_color": "blue", - "public_ip_color": "red", - "locale_label_color": "blue", - "locale_value_color": "cyan", - "mac_label_color": "blue", - "mac_value_color": "blue", - "upload_label_color": "blue", - "upload_value_color": "bright_red", - "download_label_color": "blue", - "download_value_color": "blue" + "show_public_ip": false, + "show_locale": false, + "show_mac": false, + "show_upload": false, + "show_download": false, + "#-": "cyan", + "~": "white", + ":": "white", + "separator_line": "cyan", + "header_text_color": "bright_cyan", + "name_label_color": "white", + "name_value_color": "bright_cyan", + "type_label_color": "white", + "type_value_color": "bright_cyan", + "local_ip_label_color": "white", + "local_ip_color": "bright_cyan", + "public_ip_label_color": "white", + "public_ip_color": "bright_cyan" }, "dummy_network_info": { - "enabled": false, - "show_header": true, - "show_name": true, - "show_type": true, - "show_local_ip": true, - "show_public_ip": true, - "show_locale": true, - "show_mac": true, - "show_upload": true, - "show_download": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "separator_line": "red", - "header_text_color": "bright_yellow", - "name_label_color": "blue", - "name_value_color": "blue", - "type_label_color": "blue", - "type_value_color": "cyan", - "local_ip_label_color": "blue", - "local_ip_color": "red", - "public_ip_label_color": "blue", - "public_ip_color": "red", - "locale_label_color": "blue", - "locale_value_color": "cyan", - "mac_label_color": "blue", - "mac_value_color": "blue", - "upload_label_color": "blue", - "upload_value_color": "bright_red", - "download_label_color": "blue", - "download_value_color": "blue" + "enabled": false }, "os_info": { "enabled": true, @@ -483,27 +329,23 @@ "show_architecture": true, "show_kernel": true, "show_uptime": true, - "show_install_date": true, - "show_serial": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "separator_line": "red", - "header_text_color": "bright_yellow", - "name_label_color": "blue", - "name_value_color": "blue", - "build_label_color": "blue", - "build_value_color": "red", - "arch_label_color": "blue", - "arch_value_color": "cyan", - "kernel_label_color": "blue", - "kernel_value_color": "cyan", - "uptime_label_color": "blue", - "uptime_value_color": "red", - "install_date_label_color": "blue", - "install_date_value_color": "red", - "serial_label_color": "blue", - "serial_value_color": "blue" + "show_install_date": false, + "show_serial": false, + "#-": "cyan", + "~": "white", + ":": "white", + "separator_line": "cyan", + "header_text_color": "bright_cyan", + "name_label_color": "white", + "name_value_color": "bright_cyan", + "build_label_color": "white", + "build_value_color": "bright_cyan", + "arch_label_color": "white", + "arch_value_color": "bright_cyan", + "kernel_label_color": "white", + "kernel_value_color": "bright_cyan", + "uptime_label_color": "white", + "uptime_value_color": "bright_cyan" }, "cpu_info": { "enabled": true, @@ -511,42 +353,30 @@ "show_brand": true, "show_utilization": true, "show_speed": true, - "show_base_speed": true, + "show_base_speed": false, "show_cores": true, "show_logical_processors": true, - "show_sockets": true, - "show_virtualization": true, - "show_l1_cache": true, - "show_l2_cache": true, - "show_l3_cache": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "%": "red", - "separator_line": "red", - "header_text_color": "bright_yellow", - "brand_label_color": "blue", - "brand_value_color": "cyan", - "utilization_label_color": "blue", - "utilization_value_color": "red", - "speed_label_color": "blue", - "speed_value_color": "cyan", - "base_speed_label_color": "blue", - "base_speed_value_color": "blue", - "cores_label_color": "blue", - "cores_value_color": "cyan", - "logical_processors_label_color": "blue", - "logical_processors_value_color": "bright_cyan", - "sockets_label_color": "blue", - "sockets_value_color": "blue", - "virtualization_label_color": "blue", - "virtualization_value_color": "red", - "l1_cache_label_color": "blue", - "l1_cache_value_color": "red", - "l2_cache_label_color": "blue", - "l2_cache_value_color": "red", - "l3_cache_label_color": "blue", - "l3_cache_value_color": "cyan" + "show_sockets": false, + "show_virtualization": false, + "show_l1_cache": false, + "show_l2_cache": false, + "show_l3_cache": false, + "#-": "cyan", + "~": "white", + ":": "white", + "%": "white", + "separator_line": "cyan", + "header_text_color": "bright_cyan", + "brand_label_color": "white", + "brand_value_color": "bright_cyan", + "utilization_label_color": "white", + "utilization_value_color": "bright_cyan", + "speed_label_color": "white", + "speed_value_color": "bright_cyan", + "cores_label_color": "white", + "cores_value_color": "bright_cyan", + "logical_processors_label_color": "white", + "logical_processors_value_color": "bright_cyan" }, "gpu_info": { "enabled": true, @@ -558,178 +388,75 @@ "show_vendor": true, "show_driver": true, "show_temperature": true, - "show_cores": true, - "show_primary_details": true, - "#-": "bright_blue", - "|->": "cyan", - "#->": "bright_cyan", - ":": "red", - "%": "red", - "unit_color": "cyan", - "separator_line": "red", + "show_cores": false, + "show_primary_details": false, + "#-": "cyan", + "|->": "white", + "#->": "white", + ":": "white", + "%": "white", + "unit_color": "white", + "separator_line": "cyan", "header_text_color": "bright_cyan", - "primary_header_color": "bright_blue", - "gpu_index_label_color": "blue", - "name_label_color": "blue", - "name_value_color": "cyan", - "memory_label_color": "blue", - "memory_value_color": "bright_blue", - "usage_label_color": "blue", - "usage_value_color": "bright_red", - "vendor_label_color": "blue", - "vendor_value_color": "cyan", - "driver_label_color": "blue", + "primary_header_color": "bright_cyan", + "gpu_index_label_color": "white", + "name_label_color": "white", + "name_value_color": "bright_cyan", + "memory_label_color": "white", + "memory_value_color": "bright_cyan", + "usage_label_color": "white", + "usage_value_color": "bright_cyan", + "vendor_label_color": "white", + "vendor_value_color": "bright_cyan", + "driver_label_color": "white", "driver_value_color": "bright_cyan", - "temp_label_color": "blue", - "temp_value_color": "bright_red", - "cores_label_color": "blue", - "cores_value_color": "cyan", - "p_name_label_color": "blue", - "p_vram_label_color": "blue", - "p_freq_label_color": "blue", - "freq_value_color": "bright_blue", - "error_color": "bright_red" + "temp_label_color": "white", + "temp_value_color": "bright_cyan" }, "display_info": { "enabled": true, - "show_display_banner": true, "show_display_index": false, "show_name": true, "show_applied_resolution": true, "show_native_resolution": true, "show_aspect_ratio": true, - "show_scaling": true, - "show_upscale": true, - "show_dsr": true, - "display_banner_text": "blue", - "display_banner_line": "red", - + "show_scaling": false, + "show_upscale": false, + "show_dsr": false, + "display_banner_text": "cyan", + "display_banner_line": "cyan", "colors": { - "#-": "blue", - "|->": "cyan", - ":": "blue", - "x": "blue", - "@": "blue", - "%": "blue", - - "header_text_color": "cyan", - "separator_line": "red", - - "index_color": "cyan", - - "name_label_color": "blue", - "name_value_color": "cyan", - - "applied_res_label_color": "blue", - "applied_res_value_color": "cyan", - - "native_res_label_color": "blue", - "native_res_value_color": "cyan", - - "aspect_ratio_label_color": "blue", - "aspect_ratio_value_color": "cyan", - - "scaling_label_color": "blue", - "scaling_value_color": "cyan", - - "upscale_label_color": "blue", - "upscale_value_color": "cyan", - - "hz_color": "red", - - "dsr_label_color": "blue", - "dsr_enabled_color": "cyan", - "dsr_disabled_color": "red", - "dsr_brackets_color": "blue", - "dsr_type_color": "cyan" + "#-": "cyan", + "|->": "white", + ":": "white", + "x": "white", + "@": "white", + "%": "white", + "header_text_color": "bright_cyan", + "separator_line": "cyan", + "index_color": "bright_cyan", + "name_label_color": "white", + "name_value_color": "bright_cyan", + "applied_res_label_color": "white", + "applied_res_value_color": "bright_cyan", + "native_res_label_color": "white", + "native_res_value_color": "bright_cyan", + "aspect_ratio_label_color": "white", + "aspect_ratio_value_color": "bright_cyan", + "hz_color": "bright_cyan" } - }, "bios_mb_info": { - "enabled": true, - "show_header": true, - "show_bios_vendor": true, - "show_bios_version": true, - "show_bios_date": true, - "show_mb_model": true, - "show_mb_manufacturer": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "separator_line": "red", - "header_text_color": "bright_cyan", - "vendor_label_color": "blue", - "vendor_value_color": "cyan", - "version_label_color": "blue", - "version_value_color": "bright_cyan", - "date_label_color": "blue", - "date_value_color": "bright_red", - "model_label_color": "blue", - "model_value_color": "bright_blue", - "mfg_label_color": "blue", - "mfg_value_color": "cyan" + "enabled": false }, "user_info": { - "enabled": true, - "show_header": true, - "show_username": true, - "show_computer_name": true, - "show_domain": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "separator_line": "red", - "header_text_color": "bright_cyan", - "username_label_color": "blue", - "username_value_color": "cyan", - "computer_name_label_color": "blue", - "computer_name_value_color": "red", - "domain_label_color": "blue", - "domain_value_color": "bright_blue" + "enabled": false }, "performance_info": { - "enabled": true, - "show_header": true, - "show_uptime": true, - "show_cpu_usage": true, - "show_ram_usage": true, - "show_disk_usage": true, - "show_gpu_usage": true, - "#-": "bright_blue", - "~": "cyan", - ":": "red", - "%": "blue", - "separator_line": "red", - "header_text_color": "bright_cyan", - "uptime_label_color": "blue", - "uptime_value_color": "bright_blue", - "cpu_usage_label_color": "blue", - "ram_usage_label_color": "blue", - "disk_usage_label_color": "blue", - "gpu_usage_label_color": "blue", - "usage_value_color": "bright_red" + "enabled": false }, "audio_power_info": { - "enabled": true, - "show_output_header": true, - "show_input_header": true, - "show_active_status": true, - "show_power_info": true, - "show_power_header": true, - "#-": "bright_blue", - "~": "cyan", - "separator_line": "red", - "bracket_color": "blue", - "unit_color": "bright_cyan", - "header_text_color": "bright_cyan", - "battery_label_color": "blue", - "index_color": "red", - "device_name_color": "cyan", - "active_label_color": "red", - "wired_text_color": "bright_blue", - "battery_percent_color": "bright_blue", - "charging_status_color": "bright_cyan", - "not_charging_status_color": "bright_red" + "enabled": false } -} \ No newline at end of file +} diff --git a/project_binary_fetch/binary_fetch_v1/DetailedScreen.h b/project_binary_fetch/binary_fetch_v1/DetailedScreen.h index dd3d927..734c454 100644 --- a/project_binary_fetch/binary_fetch_v1/DetailedScreen.h +++ b/project_binary_fetch/binary_fetch_v1/DetailedScreen.h @@ -1,7 +1,10 @@ #pragma once #include #include + +#if defined(_WIN32) || defined(_WIN64) #include +#endif struct DetailedScreenInfo { // Basic Information @@ -80,14 +83,13 @@ class DetailedScreen { private: std::vector screens; - // Core population methods +#if defined(_WIN32) || defined(_WIN64) bool populateFromDXGI(); bool enrichWithEDID(); bool enrichWithRegistry(); bool enrichWithNVAPI(); bool enrichWithADL(); - // Enhanced EDID parsing struct ExtendedEDIDInfo { std::string friendlyName; int nativeWidth; @@ -101,7 +103,6 @@ class DetailedScreen { ExtendedEDIDInfo parseExtendedEDID(const unsigned char* edid, size_t size); - // Helper methods std::string getFriendlyNameFromEDID(const std::wstring& deviceName); std::string getConnectionType(const std::wstring& deviceName); bool detectHDRCapability(const std::wstring& deviceName); @@ -110,7 +111,9 @@ class DetailedScreen { int getBitDepth(const std::wstring& deviceName); std::string getColorFormat(const std::wstring& deviceName); std::string getPanelType(const std::string& modelName); - - // Manufacturer ID decoder std::string decodeManufacturerID(unsigned short id); +#else + bool populateFromXrandr(); + bool populateFromDRM(); +#endif }; \ No newline at end of file diff --git a/project_binary_fetch/binary_fetch_v1/DistroDetector.cpp b/project_binary_fetch/binary_fetch_v1/DistroDetector.cpp new file mode 100644 index 0000000..40a30f1 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/DistroDetector.cpp @@ -0,0 +1,349 @@ +#include "DistroDetector.h" +#include +#include +#include +#include + +#if defined(_WIN32) +#define PLATFORM_WINDOWS 1 +#else +#define PLATFORM_WINDOWS 0 +#endif + +#if defined(__linux__) +#define PLATFORM_LINUX 1 +#else +#define PLATFORM_LINUX 0 +#endif + +#if defined(__FreeBSD__) +#define PLATFORM_FREEBSD 1 +#else +#define PLATFORM_FREEBSD 0 +#endif + +#if defined(__APPLE__) +#define PLATFORM_MACOS 1 +#else +#define PLATFORM_MACOS 0 +#endif + +static std::string toLower(const std::string& s) { + std::string result = s; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::tolower(c); }); + return result; +} + +std::string DistroDetector::readOsRelease() { + std::ifstream file("/etc/os-release"); + if (!file.is_open()) { + file.open("/usr/lib/os-release"); + } + if (!file.is_open()) return ""; + + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +Distro DistroDetector::detectLinux() { + std::string osRelease = toLower(readOsRelease()); + + if (osRelease.find("nixos") != std::string::npos) return Distro::NixOS; + if (osRelease.find("endeavouros") != std::string::npos) return Distro::EndeavourOS; + if (osRelease.find("garuda") != std::string::npos) return Distro::Garuda; + if (osRelease.find("manjaro") != std::string::npos) return Distro::Manjaro; + if (osRelease.find("artix") != std::string::npos) return Distro::Artix; + if (osRelease.find("arch") != std::string::npos) return Distro::Arch; + if (osRelease.find("pop") != std::string::npos) return Distro::PopOS; + if (osRelease.find("elementary") != std::string::npos) return Distro::Elementary; + if (osRelease.find("zorin") != std::string::npos) return Distro::Zorin; + if (osRelease.find("mint") != std::string::npos) return Distro::Mint; + if (osRelease.find("kali") != std::string::npos) return Distro::Kali; + if (osRelease.find("parrot") != std::string::npos) return Distro::ParrotOS; + if (osRelease.find("ubuntu") != std::string::npos) return Distro::Ubuntu; + if (osRelease.find("debian") != std::string::npos) return Distro::Debian; + if (osRelease.find("fedora") != std::string::npos) return Distro::Fedora; + if (osRelease.find("centos") != std::string::npos) return Distro::CentOS; + if (osRelease.find("red hat") != std::string::npos || + osRelease.find("rhel") != std::string::npos) return Distro::RHEL; + if (osRelease.find("opensuse") != std::string::npos || + osRelease.find("suse") != std::string::npos) return Distro::OpenSUSE; + if (osRelease.find("gentoo") != std::string::npos) return Distro::Gentoo; + if (osRelease.find("slackware") != std::string::npos) return Distro::Slackware; + if (osRelease.find("alpine") != std::string::npos) return Distro::Alpine; + if (osRelease.find("void") != std::string::npos) return Distro::Void; + if (osRelease.find("mx") != std::string::npos) return Distro::MXLinux; + + return Distro::Unknown; +} + +Distro DistroDetector::detectBSD() { +#if PLATFORM_FREEBSD + return Distro::FreeBSD; +#endif + + std::ifstream file("/etc/rc.conf"); + if (file.is_open()) { + return Distro::FreeBSD; + } + + return Distro::Unknown; +} + +Distro DistroDetector::detect() { +#if PLATFORM_WINDOWS + return Distro::Windows; +#elif PLATFORM_MACOS + return Distro::macOS; +#elif PLATFORM_FREEBSD + return Distro::FreeBSD; +#elif PLATFORM_LINUX + return detectLinux(); +#else + return Distro::Unknown; +#endif +} + +std::string DistroDetector::getName(Distro d) { + switch (d) { + case Distro::Arch: return "Arch Linux"; + case Distro::Debian: return "Debian"; + case Distro::Ubuntu: return "Ubuntu"; + case Distro::Fedora: return "Fedora"; + case Distro::CentOS: return "CentOS"; + case Distro::RHEL: return "Red Hat"; + case Distro::OpenSUSE: return "openSUSE"; + case Distro::Manjaro: return "Manjaro"; + case Distro::Mint: return "Linux Mint"; + case Distro::PopOS: return "Pop!_OS"; + case Distro::Gentoo: return "Gentoo"; + case Distro::Slackware: return "Slackware"; + case Distro::Alpine: return "Alpine"; + case Distro::Void: return "Void Linux"; + case Distro::NixOS: return "NixOS"; + case Distro::EndeavourOS: return "EndeavourOS"; + case Distro::Garuda: return "Garuda Linux"; + case Distro::Kali: return "Kali Linux"; + case Distro::ParrotOS: return "Parrot OS"; + case Distro::Zorin: return "Zorin OS"; + case Distro::Elementary: return "elementary OS"; + case Distro::MXLinux: return "MX Linux"; + case Distro::Artix: return "Artix Linux"; + case Distro::FreeBSD: return "FreeBSD"; + case Distro::OpenBSD: return "OpenBSD"; + case Distro::NetBSD: return "NetBSD"; + case Distro::DragonFlyBSD: return "DragonFly BSD"; + case Distro::macOS: return "macOS"; + case Distro::Windows: return "Windows"; + default: return "Linux"; + } +} + +std::string DistroDetector::getAsciiArt(Distro d) { + switch (d) { + case Distro::NixOS: + return R"($6 \\ \\ // +$6 ==\\__\\/ // +$6 // \\// +$6==// //== +$6 //\\___// +$6// /\\ \\== +$6 // \\ \\)"; + + case Distro::Arch: + return R"($6 /\ +$6 / \ +$6 /\ \ +$6 / \ +$6 / ,, \ +$6 / | | -\ +$6/_-'' ''-_\)"; + + case Distro::Debian: + return R"($1 _____ +$1 / __ \ +$1| / | +$1| \___- +$1-_ +$1 --_)"; + + case Distro::Ubuntu: + return R"($3 _ +$3 ---(_) +$3 _/ --- \ +$3(_) | | +$3 \ --- _/ +$3 ---(_))"; + + case Distro::Fedora: + return R"($4 _____ +$4 / __)$7\ +$4 | / $7\ \ +$7 __$4_| |_$7_/ / +$7 / $4(_ _)$7_/ +$7 / / $4| | +$7 \ \$4__/ | +$7 \$4(_____/)"; + + case Distro::Manjaro: + return R"($2||||||||| |||| +$2||||||||| |||| +$2|||| |||| +$2|||| |||| |||| +$2|||| |||| |||| +$2|||| |||| |||| +$2|||| |||| ||||)"; + + case Distro::Mint: + return R"($2 _____________ +$2|_ \\ +$2 | $7| _____ $2| +$2 | $7| | | | $2| +$2 | $7| | | | $2| +$2 | $7\\__$7___/ $2| +$2 \\_________/)"; + + case Distro::PopOS: + return R"($6______ +$6\ _ \ __ +$6 \ \ \ \ / / +$6 \ \_\ \ / / +$6 \ ___\ /_/ +$6 \ \ _ +$6 __\_\__(_)_ +$6 (___________)"; + + case Distro::Gentoo: + return R"($5 _-----_ +$5( \\ +$5\ 0 \\ +$7 \ ) +$7 / _/ +$7( _- +$7\____-)"; + + case Distro::Alpine: + return R"($4 /\ /\ +$4 / \ \ +$4 / \ \ +$4/ \ \ +$4\ \ / +$4 \ / +$4 \ /)"; + + case Distro::Void: + return R"($2 _______ +$2 _ \______ - +$2| \ ___ \ | +$2| | / \ | | +$2| | \___/ | | +$2| \______ \_| +$2 -_______\)"; + + case Distro::EndeavourOS: + return R"($5 /$1\ +$5 /$1/ \$5\ +$5 /$1/ $5/$1\ \$5\ +$5 /$1/ $5/$6 \$1\ \$5\ +$5 /$1/ $5/$6 \$1\ \$5\ +$5/$1/ $6 \$1\ \$5\ +$5\$1\$6 /$1/ $5/ +$5 \$1\$6_________/$1/ $5/)"; + + case Distro::Garuda: + return R"($6 .. +$6 .;;,. +$6 ';;;;;;;. +$6 ':;;;;;;;;;;, +$6 .:;;;;;;;$1''$6;;;' +$6 ';;;;;;$1'$6 .$1';;$6 +$6.;;;$1'. .$6;;;$1'. +$6;;$1' .;;;;;$1'.)"; + + case Distro::Kali: + return R"($4.............. +$4 ..,;:ccc,. +$4 ......''';lxO. +$4.....''''..........,:ld; +$4 .';;;:::;,,.x, +$4 ..'''. 0Kx +$4 .... KKK)"; + + case Distro::Artix: + return R"($6 /\ +$6 / \ +$6 /`'.,\ +$6 / ', +$6 / ,`\ +$6 / ,.'`. \ +$6/.,'` `'.\)"; + + case Distro::FreeBSD: + return R"($1 /\,-'''''-,/\ +$1 \_) (_/ +$1 | \ / | +$1 | O O | +$1 ; ._, ; +$1 '-___-')"; + + case Distro::OpenBSD: + return R"($3 _____ +$3 \- -/ +$3 \_/ \ +$3 | O O | +$3 |_ < ) 3 ) +$3 / \ / +$3 /-_____-\)"; + + case Distro::macOS: + return R"($2 .:' +$2 __ :'__ +$3 .'` `-' ``. +$1: .-' +$1: : +$5 : `-; +$4 `.__.-.__.')"; + + case Distro::Windows: + return R"($6lllllllll lllllllll +$6lllllllll lllllllll +$6lllllllll lllllllll +$6lllllllll lllllllll + +$6lllllllll lllllllll +$6lllllllll lllllllll +$6lllllllll lllllllll +$6lllllllll lllllllll)"; + + case Distro::OpenSUSE: + return R"($2 _______ +$2__| __ \ +$2 / .\\ \\ +$2 \\__/ | +$2 _______| +$2 \_______ +$2__________/)"; + + case Distro::CentOS: + case Distro::RHEL: + return R"($2 .---. +$3 / \\ +$3 \\ / +$2 /`--.--'\\ +$4 / $2.'o'.$4 \\ +$4 / `-----' \\ +$4 \\ / +$4 `--.___,--')"; + + default: + return R"($7 ___ +$7 (.· | +$7 (<> | +$7 / __ \ +$7 ( / \ /| +$7_/\ __)/_) +$7\/-____\/)"; + } +} diff --git a/project_binary_fetch/binary_fetch_v1/DistroDetector.h b/project_binary_fetch/binary_fetch_v1/DistroDetector.h new file mode 100644 index 0000000..a657788 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/DistroDetector.h @@ -0,0 +1,28 @@ +#ifndef DISTRO_DETECTOR_H +#define DISTRO_DETECTOR_H + +#include + +enum class Distro { + Unknown, + Arch, Debian, Ubuntu, Fedora, CentOS, RHEL, OpenSUSE, Manjaro, + Mint, PopOS, Gentoo, Slackware, Alpine, Void, NixOS, EndeavourOS, + Garuda, Kali, ParrotOS, Zorin, Elementary, MXLinux, Artix, + FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, + macOS, + Windows +}; + +class DistroDetector { +public: + static Distro detect(); + static std::string getName(Distro d); + static std::string getAsciiArt(Distro d); + +private: + static Distro detectLinux(); + static Distro detectBSD(); + static std::string readOsRelease(); +}; + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/PerformanceInfo.h b/project_binary_fetch/binary_fetch_v1/PerformanceInfo.h index 8151ae7..5b01c5a 100644 --- a/project_binary_fetch/binary_fetch_v1/PerformanceInfo.h +++ b/project_binary_fetch/binary_fetch_v1/PerformanceInfo.h @@ -1,11 +1,13 @@ #pragma once #include + +#if defined(_WIN32) || defined(_WIN64) #include #include - #pragma comment(lib, "pdh.lib") -#pragma comment(lib, "nvapi64.lib") // make sure NVAPI SDK library is linked +#pragma comment(lib, "nvapi64.lib") +#endif class PerformanceInfo { private: diff --git a/project_binary_fetch/binary_fetch_v1/TimeInfo.cpp b/project_binary_fetch/binary_fetch_v1/TimeInfo.cpp index 8a80363..b00ddff 100644 --- a/project_binary_fetch/binary_fetch_v1/TimeInfo.cpp +++ b/project_binary_fetch/binary_fetch_v1/TimeInfo.cpp @@ -1,23 +1,24 @@ #include "TimeInfo.h" -// Constructor - gets current system time TimeInfo::TimeInfo() { updateTime(); } -// Private method to fetch current system time void TimeInfo::updateTime() { +#if defined(_WIN32) || defined(_WIN64) GetLocalTime(&systemTime); +#else + time_t now = time(nullptr); + localtime_r(&now, &timeInfo); +#endif } -// Check if a year is a leap year bool TimeInfo::isLeapYear(int year) const { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } -// Calculate ISO week number int TimeInfo::calculateWeekNumber() const { - // Get January 1st of current year +#if defined(_WIN32) || defined(_WIN64) SYSTEMTIME jan1; jan1.wYear = systemTime.wYear; jan1.wMonth = 1; @@ -37,72 +38,98 @@ int TimeInfo::calculateWeekNumber() const { uli2.LowPart = ft2.dwLowDateTime; uli2.HighPart = ft2.dwHighDateTime; - // Calculate day of year ULONGLONG diff = uli2.QuadPart - uli1.QuadPart; int dayOfYear = (int)(diff / 10000000ULL / 86400ULL) + 1; - - // Calculate week number (simple calculation) int weekNumber = (dayOfYear + 6) / 7; - return weekNumber; +#else + int dayOfYear = timeInfo.tm_yday + 1; + int weekNumber = (dayOfYear + 6) / 7; + return weekNumber; +#endif } -// Get second int TimeInfo::getSecond() const { +#if defined(_WIN32) || defined(_WIN64) return systemTime.wSecond; +#else + return timeInfo.tm_sec; +#endif } -// Get minute int TimeInfo::getMinute() const { +#if defined(_WIN32) || defined(_WIN64) return systemTime.wMinute; +#else + return timeInfo.tm_min; +#endif } -// Get hour int TimeInfo::getHour() const { +#if defined(_WIN32) || defined(_WIN64) return systemTime.wHour; +#else + return timeInfo.tm_hour; +#endif } -// Get day int TimeInfo::getDay() const { +#if defined(_WIN32) || defined(_WIN64) return systemTime.wDay; +#else + return timeInfo.tm_mday; +#endif } -// Get week number int TimeInfo::getWeekNumber() const { return calculateWeekNumber(); } -// Get day name std::string TimeInfo::getDayName() const { const char* days[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +#if defined(_WIN32) || defined(_WIN64) return days[systemTime.wDayOfWeek]; +#else + return days[timeInfo.tm_wday]; +#endif } -// Get month number int TimeInfo::getMonthNumber() const { +#if defined(_WIN32) || defined(_WIN64) return systemTime.wMonth; +#else + return timeInfo.tm_mon + 1; +#endif } -// Get month name std::string TimeInfo::getMonthName() const { const char* months[] = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; +#if defined(_WIN32) || defined(_WIN64) return months[systemTime.wMonth]; +#else + return months[timeInfo.tm_mon + 1]; +#endif } -// Get year number int TimeInfo::getYearNumber() const { +#if defined(_WIN32) || defined(_WIN64) return systemTime.wYear; +#else + return timeInfo.tm_year + 1900; +#endif } -// Get leap year status std::string TimeInfo::getLeapYear() const { +#if defined(_WIN32) || defined(_WIN64) return isLeapYear(systemTime.wYear) ? "Yes" : "No"; +#else + return isLeapYear(timeInfo.tm_year + 1900) ? "Yes" : "No"; +#endif } -// Refresh the system time void TimeInfo::refresh() { updateTime(); -} \ No newline at end of file +} diff --git a/project_binary_fetch/binary_fetch_v1/TimeInfo.h b/project_binary_fetch/binary_fetch_v1/TimeInfo.h index ae968d1..21985bf 100644 --- a/project_binary_fetch/binary_fetch_v1/TimeInfo.h +++ b/project_binary_fetch/binary_fetch_v1/TimeInfo.h @@ -2,35 +2,39 @@ #define TIMEINFO_H #include +#include + +#if defined(_WIN32) || defined(_WIN64) #include +#endif class TimeInfo { private: +#if defined(_WIN32) || defined(_WIN64) SYSTEMTIME systemTime; +#else + struct tm timeInfo; +#endif void updateTime(); bool isLeapYear(int year) const; int calculateWeekNumber() const; public: - // Constructor - automatically fetches current system time TimeInfo(); - // Time components int getSecond() const; int getMinute() const; int getHour() const; - // Date components int getDay() const; int getWeekNumber() const; std::string getDayName() const; int getMonthNumber() const; std::string getMonthName() const; int getYearNumber() const; - std::string getLeapYear() const; // Returns "Yes" or "No" + std::string getLeapYear() const; - // Refresh system time void refresh(); }; diff --git a/project_binary_fetch/binary_fetch_v1/build/binaryfetch b/project_binary_fetch/binary_fetch_v1/build/binaryfetch new file mode 100755 index 0000000..a18b299 Binary files /dev/null and b/project_binary_fetch/binary_fetch_v1/build/binaryfetch differ diff --git a/project_binary_fetch/binary_fetch_v1/main.cpp b/project_binary_fetch/binary_fetch_v1/main.cpp index 9a6837c..d0b79cd 100644 --- a/project_binary_fetch/binary_fetch_v1/main.cpp +++ b/project_binary_fetch/binary_fetch_v1/main.cpp @@ -1,35 +1,44 @@ -// main.cpp (AsciiArt separated into header and implementation files) +// main.cpp (AsciiArt separated into header and implementation files) #include -#include // Formatting utilities (setw, precision) +#include #include #include -#include // For string stream operations +#include #include #include -#include #include -#include -#include -#include -#include -#include -// ASCII Art functionality -#include "AsciiArt.h" // main.cpp (AsciiArt separated into header and implementation files) +#include + +// Platform-specific includes +#if defined(_WIN32) || defined(_WIN64) + #define PLATFORM_WINDOWS 1 + #include + #include + #include + #include + #include +#else + #define PLATFORM_WINDOWS 0 + #include + #include + #include +#endif + +#if defined(__linux__) + #define PLATFORM_LINUX 1 +#else + #define PLATFORM_LINUX 0 +#endif + +#if defined(__FreeBSD__) + #define PLATFORM_FREEBSD 1 +#else + #define PLATFORM_FREEBSD 0 +#endif + +#define PLATFORM_POSIX (PLATFORM_LINUX || PLATFORM_FREEBSD) -#include -#include // Formatting utilities (setw, precision) -#include -#include -#include // For string stream operations -#include -#include -#include -#include -#include -#include -#include -// ASCII Art functionality #include "AsciiArt.h" // ------------------ Full System Info Modules ------------------ @@ -92,60 +101,82 @@ it is a sign that the logic should be moved into a new module. */ -int main(){ +int main(int argc, char* argv[]){ + + bool fullMode = false; + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--full" || arg == "-f") { + fullMode = true; + } else if (arg == "--help" || arg == "-h") { + std::cout << "BinaryFetch - System Information Tool\n\n" + << "Usage: binaryfetch [OPTIONS]\n\n" + << "Options:\n" + << " -f, --full Show detailed system information (expanded mode)\n" + << " -h, --help Show this help message\n\n" + << "Config files:\n" +#if PLATFORM_WINDOWS + << " C:\\Users\\Public\\BinaryFetch\\BinaryFetch_Config.json\n" + << " C:\\Users\\Public\\BinaryFetch\\BinaryArt.txt\n" +#else + << " ~/.config/binaryfetch/BinaryFetch_Config.json\n" + << " ~/.config/binaryfetch/BinaryArt.txt\n" +#endif + << std::endl; + return 0; + } + } - // Initialize COM +#if PLATFORM_WINDOWS HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hr)) { std::cout << "Failed to initialize COM library. Error: 0x" << std::hex << hr << std::endl; return 1; } + SetConsoleOutputCP(CP_UTF8); +#endif - - - - // ========== SIMPLIFIED ASCII ART LOADING ========== - // Just call loadFromFile() - it handles everything automatically! - // - Checks C:\Users\\AppData\BinaryFetch\BinaryArt.txt - // - If missing, copies from Default_Ascii_Art.txt and creates it - // - User can modify their art anytime in AppData folder - - SetConsoleOutputCP(CP_UTF8); // UTF-8 output on Windows console (for emoji printing) AsciiArt art; if (!art.loadFromFile()) { std::cout << "Warning: ASCII art could not be loaded. Continuing without art.\n"; - // Program continues even if art fails to load } - // ========== AUTO CONFIG FILE SETUP ========== - // true = dev mode (loads local file), false = production mode (extracts from EXE) bool LOAD_DEFAULT_CONFIG = false; +#if PLATFORM_WINDOWS std::string configDir = "C:\\Users\\Public\\BinaryFetch"; std::string userConfigPath = configDir + "\\BinaryFetch_Config.json"; +#else + std::string homeDir; + const char* home = getenv("HOME"); + if (home) { + homeDir = home; + } else { + struct passwd* pw = getpwuid(getuid()); + if (pw) homeDir = pw->pw_dir; + } + std::string configDir = homeDir + "/.config/binaryfetch"; + std::string userConfigPath = configDir + "/BinaryFetch_Config.json"; +#endif std::string configPath; if (LOAD_DEFAULT_CONFIG) { - // DEV MODE: Load directly from project folder for fast iteration 🧪 configPath = "Default_BinaryFetch_Config.json"; } else { - // PRODUCTION MODE: Use constant public folder 🛰️ configPath = userConfigPath; - // 1. Create directory if it doesn't exist +#if PLATFORM_WINDOWS if (GetFileAttributesA(configDir.c_str()) == INVALID_FILE_ATTRIBUTES) { _mkdir(configDir.c_str()); } - // 2. Self-Healing: Check if user config exists, if not, extract from EXE memory std::ifstream checkConfig(userConfigPath); bool userConfigExists = checkConfig.good(); checkConfig.close(); if (!userConfigExists) { - // IDR_DEFAULT_CONFIG is 101 in your resource.h HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(101), RT_RCDATA); if (hRes) { HGLOBAL hData = LoadResource(NULL, hRes); @@ -162,6 +193,36 @@ int main(){ std::cout << "Warning: Internal resource IDR_DEFAULT_CONFIG not found." << std::endl; } } +#else + struct stat st; + if (stat(configDir.c_str(), &st) != 0) { + std::string parentDir = homeDir + "/.config"; + mkdir(parentDir.c_str(), 0755); + mkdir(configDir.c_str(), 0755); + } + + std::ifstream checkConfig(userConfigPath); + bool userConfigExists = checkConfig.good(); + checkConfig.close(); + + if (!userConfigExists) { + std::ifstream defaultConfig("Default_BinaryFetch_Config.json"); + if (defaultConfig.is_open()) { + std::ofstream userConfig(userConfigPath); + userConfig << defaultConfig.rdbuf(); + userConfig.close(); + defaultConfig.close(); + } else { + std::ifstream shareConfig("/usr/share/binaryfetch/BinaryFetch_Config.json"); + if (shareConfig.is_open()) { + std::ofstream userConfig(userConfigPath); + userConfig << shareConfig.rdbuf(); + userConfig.close(); + shareConfig.close(); + } + } + } +#endif } // ========== CONFIG LOADING ========== @@ -248,7 +309,6 @@ int main(){ UserInfo user; PerformanceInfo perf; DisplayInfo di; - ExtraInfo extra; SystemInfo sys; CompactAudio c_audio; @@ -256,7 +316,6 @@ int main(){ CompactCPU c_cpu; // CompactScreen c_screen; CompactMemory c_memory; - CompactSystem c_system; CompactGPU c_gpu; CompactPerformance c_perf; CompactUser c_user; @@ -832,6 +891,7 @@ int main(){ } //-----------------------------start of detailed modules----------------------// + if (fullMode) { // ----------------- DETAILED MEMORY SECTION ----------------- // if (isEnabled("detailed_memory")) { @@ -2276,6 +2336,7 @@ int main(){ //----------------- END OF JSON-CONTROLLED COMPACT SECTIONS -----------------// + } // end fullMode @@ -2287,7 +2348,9 @@ int main(){ std::cout << std::endl; +#if PLATFORM_WINDOWS CoUninitialize(); +#endif return 0; } diff --git a/project_binary_fetch/binary_fetch_v1/platform/HttpClient.h b/project_binary_fetch/binary_fetch_v1/platform/HttpClient.h new file mode 100644 index 0000000..68a626b --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/HttpClient.h @@ -0,0 +1,221 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Platform { + +class HttpClient { +public: + struct Response { + int status_code = 0; + std::string body; + bool success = false; + }; + + static Response get(const std::string& host, const std::string& path, int port = 80, int timeout_ms = 5000) { + Response resp; + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) return resp; + + struct hostent* server = gethostbyname(host.c_str()); + if (!server) { + close(sock); + return resp; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length); + serv_addr.sin_port = htons(port); + + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); + + connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + + struct pollfd pfd; + pfd.fd = sock; + pfd.events = POLLOUT; + if (poll(&pfd, 1, timeout_ms) <= 0) { + close(sock); + return resp; + } + + fcntl(sock, F_SETFL, flags); + + std::string request = "GET " + path + " HTTP/1.1\r\n"; + request += "Host: " + host + "\r\n"; + request += "Connection: close\r\n"; + request += "User-Agent: BinaryFetch/1.0\r\n"; + request += "\r\n"; + + if (send(sock, request.c_str(), request.length(), 0) < 0) { + close(sock); + return resp; + } + + std::string response; + char buffer[4096]; + + pfd.events = POLLIN; + while (poll(&pfd, 1, timeout_ms) > 0) { + ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); + if (n <= 0) break; + buffer[n] = '\0'; + response += buffer; + } + + close(sock); + + size_t header_end = response.find("\r\n\r\n"); + if (header_end != std::string::npos) { + std::string headers = response.substr(0, header_end); + resp.body = response.substr(header_end + 4); + + size_t status_pos = headers.find(' '); + if (status_pos != std::string::npos) { + resp.status_code = std::stoi(headers.substr(status_pos + 1, 3)); + resp.success = (resp.status_code >= 200 && resp.status_code < 300); + } + } + + return resp; + } + + static Response post(const std::string& host, const std::string& path, const std::string& data, + int port = 80, int timeout_ms = 5000) { + Response resp; + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) return resp; + + struct hostent* server = gethostbyname(host.c_str()); + if (!server) { + close(sock); + return resp; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length); + serv_addr.sin_port = htons(port); + + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); + + connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + + struct pollfd pfd; + pfd.fd = sock; + pfd.events = POLLOUT; + if (poll(&pfd, 1, timeout_ms) <= 0) { + close(sock); + return resp; + } + + fcntl(sock, F_SETFL, flags); + + std::string request = "POST " + path + " HTTP/1.1\r\n"; + request += "Host: " + host + "\r\n"; + request += "Connection: close\r\n"; + request += "Content-Type: application/octet-stream\r\n"; + request += "Content-Length: " + std::to_string(data.length()) + "\r\n"; + request += "User-Agent: BinaryFetch/1.0\r\n"; + request += "\r\n"; + request += data; + + if (send(sock, request.c_str(), request.length(), 0) < 0) { + close(sock); + return resp; + } + + std::string response; + char buffer[4096]; + + pfd.events = POLLIN; + while (poll(&pfd, 1, timeout_ms) > 0) { + ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); + if (n <= 0) break; + buffer[n] = '\0'; + response += buffer; + } + + close(sock); + + size_t header_end = response.find("\r\n\r\n"); + if (header_end != std::string::npos) { + std::string headers = response.substr(0, header_end); + resp.body = response.substr(header_end + 4); + + size_t status_pos = headers.find(' '); + if (status_pos != std::string::npos) { + resp.status_code = std::stoi(headers.substr(status_pos + 1, 3)); + resp.success = (resp.status_code >= 200 && resp.status_code < 300); + } + } + + return resp; + } + + static std::string downloadSpeed(const std::string& host, const std::string& path, + size_t bytes, int timeout_ms = 5000) { + auto start = std::chrono::high_resolution_clock::now(); + Response resp = get(host, path + "?bytes=" + std::to_string(bytes), 80, timeout_ms); + auto end = std::chrono::high_resolution_clock::now(); + + if (!resp.success || resp.body.empty()) return "Unknown"; + + double seconds = std::chrono::duration(end - start).count(); + if (seconds < 0.001) seconds = 0.001; + + double megabits = (resp.body.length() * 8.0) / 1000000.0; + double mbps = megabits / seconds; + + return formatSpeed(mbps); + } + + static std::string uploadSpeed(const std::string& host, const std::string& path, + size_t bytes, int timeout_ms = 5000) { + std::string data(bytes, 'X'); + + auto start = std::chrono::high_resolution_clock::now(); + Response resp = post(host, path, data, 80, timeout_ms); + auto end = std::chrono::high_resolution_clock::now(); + + if (!resp.success) return "Unknown"; + + double seconds = std::chrono::duration(end - start).count(); + if (seconds < 0.001) seconds = 0.001; + + double megabits = (bytes * 8.0) / 1000000.0; + double mbps = megabits / seconds; + + return formatSpeed(mbps); + } + +private: + static std::string formatSpeed(double mbps) { + char buf[32]; + if (mbps >= 1000.0) { + snprintf(buf, sizeof(buf), "%.1f Gbps", mbps / 1000.0); + } else if (mbps >= 1.0) { + snprintf(buf, sizeof(buf), "%.1f Mbps", mbps); + } else { + snprintf(buf, sizeof(buf), "%.0f Kbps", mbps * 1000.0); + } + return std::string(buf); + } +}; + +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/Platform.h b/project_binary_fetch/binary_fetch_v1/platform/Platform.h new file mode 100644 index 0000000..a4d91bf --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/Platform.h @@ -0,0 +1,8 @@ +#pragma once + +#include "PlatformConfig.h" +#include "PlatformUtils.h" + +#if PLATFORM_POSIX +#include "HttpClient.h" +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/PlatformConfig.h b/project_binary_fetch/binary_fetch_v1/platform/PlatformConfig.h new file mode 100644 index 0000000..67139d8 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/PlatformConfig.h @@ -0,0 +1,28 @@ +#pragma once + +#if defined(_WIN32) || defined(_WIN64) + #define PLATFORM_WINDOWS 1 + #define PLATFORM_LINUX 0 + #define PLATFORM_FREEBSD 0 + #define PLATFORM_POSIX 0 +#elif defined(__linux__) + #define PLATFORM_WINDOWS 0 + #define PLATFORM_LINUX 1 + #define PLATFORM_FREEBSD 0 + #define PLATFORM_POSIX 1 +#elif defined(__FreeBSD__) + #define PLATFORM_WINDOWS 0 + #define PLATFORM_LINUX 0 + #define PLATFORM_FREEBSD 1 + #define PLATFORM_POSIX 1 +#else + #error "Unsupported platform" +#endif + +#if PLATFORM_WINDOWS + #define PLATFORM_NAME "Windows" +#elif PLATFORM_LINUX + #define PLATFORM_NAME "Linux" +#elif PLATFORM_FREEBSD + #define PLATFORM_NAME "FreeBSD" +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/PlatformUtils.h b/project_binary_fetch/binary_fetch_v1/platform/PlatformUtils.h new file mode 100644 index 0000000..9e07935 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/PlatformUtils.h @@ -0,0 +1,161 @@ +#pragma once + +#include "PlatformConfig.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if PLATFORM_POSIX +#include +#include +#include +#endif + +#if PLATFORM_FREEBSD +#include +#include +#endif + +namespace Platform { + +inline std::string exec(const std::string& cmd) { + std::string result; + FILE* pipe = popen(cmd.c_str(), "r"); + if (!pipe) return result; + char buffer[256]; + while (fgets(buffer, sizeof(buffer), pipe)) { + result += buffer; + } + pclose(pipe); + return result; +} + +inline std::string readFile(const std::string& path) { + std::ifstream file(path); + if (!file.is_open()) return ""; + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +inline std::string readFileLine(const std::string& path) { + std::ifstream file(path); + if (!file.is_open()) return ""; + std::string line; + std::getline(file, line); + return line; +} + +inline bool fileExists(const std::string& path) { +#if PLATFORM_WINDOWS + return false; +#else + struct stat st; + return stat(path.c_str(), &st) == 0; +#endif +} + +inline bool commandExists(const std::string& cmd) { +#if PLATFORM_WINDOWS + return false; +#else + std::string check = "command -v " + cmd + " >/dev/null 2>&1"; + return system(check.c_str()) == 0; +#endif +} + +inline std::string trim(const std::string& str) { + size_t start = str.find_first_not_of(" \t\n\r"); + if (start == std::string::npos) return ""; + size_t end = str.find_last_not_of(" \t\n\r"); + return str.substr(start, end - start + 1); +} + +inline std::vector split(const std::string& str, char delim) { + std::vector tokens; + std::istringstream iss(str); + std::string token; + while (std::getline(iss, token, delim)) { + tokens.push_back(token); + } + return tokens; +} + +inline std::string parseValue(const std::string& content, const std::string& key, char delim = ':') { + std::istringstream iss(content); + std::string line; + while (std::getline(iss, line)) { + size_t pos = line.find(key); + if (pos == 0 || (pos != std::string::npos && (pos == 0 || !std::isalnum(line[pos-1])))) { + size_t delimPos = line.find(delim, pos); + if (delimPos != std::string::npos) { + std::string value = line.substr(delimPos + 1); + value.erase(0, value.find_first_not_of(" \t\"")); + value.erase(value.find_last_not_of(" \t\n\r\"") + 1); + return value; + } + } + } + return ""; +} + +inline std::string getEnv(const std::string& name) { + const char* val = std::getenv(name.c_str()); + return val ? std::string(val) : ""; +} + +inline std::string getHomeDir() { +#if PLATFORM_POSIX + const char* home = std::getenv("HOME"); + if (home) return std::string(home); + struct passwd* pw = getpwuid(getuid()); + if (pw) return std::string(pw->pw_dir); +#endif + return ""; +} + +inline std::string getConfigDir() { +#if PLATFORM_WINDOWS + return "C:\\Users\\Public\\BinaryFetch"; +#else + std::string xdg = getEnv("XDG_CONFIG_HOME"); + if (!xdg.empty()) return xdg + "/binaryfetch"; + return getHomeDir() + "/.config/binaryfetch"; +#endif +} + +#if PLATFORM_FREEBSD +inline std::string sysctlString(const std::string& name) { + char buf[256] = {0}; + size_t len = sizeof(buf); + if (sysctlbyname(name.c_str(), buf, &len, nullptr, 0) == 0) { + return std::string(buf); + } + return ""; +} + +inline long sysctlLong(const std::string& name) { + long val = 0; + size_t len = sizeof(val); + if (sysctlbyname(name.c_str(), &val, &len, nullptr, 0) == 0) { + return val; + } + return 0; +} + +inline unsigned long sysctlULong(const std::string& name) { + unsigned long val = 0; + size_t len = sizeof(val); + if (sysctlbyname(name.c_str(), &val, &len, nullptr, 0) == 0) { + return val; + } + return 0; +} +#endif + +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/AudioInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/AudioInfoFreeBSD.cpp new file mode 100644 index 0000000..9388687 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/AudioInfoFreeBSD.cpp @@ -0,0 +1,95 @@ +#include "../Platform.h" +#include "../../CompactAudio.h" +#include +#include + +static std::string cachedOutputDevice; +static std::string cachedInputDevice; +static bool devicesCached = false; + +static void cacheAudioDevices() { + if (devicesCached) return; + + std::string mixerOutput = Platform::exec("mixer -s 2>/dev/null"); + + if (!mixerOutput.empty()) { + std::istringstream iss(mixerOutput); + std::string line; + bool foundOutput = false; + bool foundInput = false; + + while (std::getline(iss, line)) { + if (line.find("vol") != std::string::npos && !foundOutput) { + cachedOutputDevice = "OSS Audio Output"; + foundOutput = true; + } + if (line.find("mic") != std::string::npos && !foundInput) { + cachedInputDevice = "OSS Microphone"; + foundInput = true; + } + if (line.find("rec") != std::string::npos && !foundInput) { + cachedInputDevice = "OSS Recording Input"; + foundInput = true; + } + } + } + + if (cachedOutputDevice.empty()) { + std::string sndstat = Platform::readFile("/dev/sndstat"); + if (!sndstat.empty()) { + std::istringstream iss(sndstat); + std::string line; + while (std::getline(iss, line)) { + if (line.find("pcm") != std::string::npos && line.find("play") != std::string::npos) { + size_t bracket = line.find('<'); + size_t bracket2 = line.find('>'); + if (bracket != std::string::npos && bracket2 != std::string::npos) { + cachedOutputDevice = line.substr(bracket + 1, bracket2 - bracket - 1); + break; + } + } + } + } + } + + if (cachedInputDevice.empty()) { + std::string sndstat = Platform::readFile("/dev/sndstat"); + if (!sndstat.empty()) { + std::istringstream iss(sndstat); + std::string line; + while (std::getline(iss, line)) { + if (line.find("pcm") != std::string::npos && line.find("rec") != std::string::npos) { + size_t bracket = line.find('<'); + size_t bracket2 = line.find('>'); + if (bracket != std::string::npos && bracket2 != std::string::npos) { + cachedInputDevice = line.substr(bracket + 1, bracket2 - bracket - 1); + break; + } + } + } + } + } + + if (cachedOutputDevice.empty()) cachedOutputDevice = "Unknown Audio Output"; + if (cachedInputDevice.empty()) cachedInputDevice = "Unknown Audio Input"; + + devicesCached = true; +} + +std::string CompactAudio::active_audio_output() { + cacheAudioDevices(); + return cachedOutputDevice; +} + +std::string CompactAudio::active_audio_output_status() { + return "Active"; +} + +std::string CompactAudio::active_audio_input() { + cacheAudioDevices(); + return cachedInputDevice; +} + +std::string CompactAudio::active_audio_input_status() { + return "Active"; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/CPUInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/CPUInfoFreeBSD.cpp new file mode 100644 index 0000000..0584bf4 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/CPUInfoFreeBSD.cpp @@ -0,0 +1,235 @@ +#include "../Platform.h" +#include "../../CPUInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static long prev_cp_time[5] = {0}; +static bool first_call = true; + +std::string CPUInfo::get_cpu_info() { + std::string model = Platform::sysctlString("hw.model"); + return model.empty() ? "Unknown CPU" : model; +} + +float CPUInfo::get_cpu_utilization() { + long cp_time[5]; + size_t len = sizeof(cp_time); + + if (sysctlbyname("kern.cp_time", cp_time, &len, nullptr, 0) != 0) { + return 0.0f; + } + + if (first_call) { + memcpy(prev_cp_time, cp_time, sizeof(cp_time)); + first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return get_cpu_utilization(); + } + + long total_delta = 0; + long idle_delta = 0; + + for (int i = 0; i < 5; i++) { + total_delta += cp_time[i] - prev_cp_time[i]; + } + idle_delta = cp_time[4] - prev_cp_time[4]; + + memcpy(prev_cp_time, cp_time, sizeof(cp_time)); + + if (total_delta == 0) return 0.0f; + + return (1.0f - (float)idle_delta / (float)total_delta) * 100.0f; +} + +std::string CPUInfo::get_cpu_base_speed() { + std::string freq = Platform::exec("sysctl -n dev.cpu.0.freq 2>/dev/null"); + freq = Platform::trim(freq); + + if (!freq.empty()) { + try { + float mhz = std::stof(freq); + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << (mhz / 1000.0f) << " GHz"; + return ss.str(); + } catch (...) {} + } + + std::string result = Platform::exec("sysctl -n hw.clockrate 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty()) { + try { + float mhz = std::stof(result); + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << (mhz / 1000.0f) << " GHz"; + return ss.str(); + } catch (...) {} + } + + return "N/A"; +} + +std::string CPUInfo::get_cpu_speed() { + std::string freq = Platform::exec("sysctl -n dev.cpu.0.freq 2>/dev/null"); + freq = Platform::trim(freq); + + if (!freq.empty()) { + try { + float mhz = std::stof(freq); + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << (mhz / 1000.0f) << " GHz"; + return ss.str(); + } catch (...) {} + } + + return get_cpu_base_speed(); +} + +int CPUInfo::get_cpu_sockets() { + long packages = Platform::sysctlLong("hw.packages"); + return packages > 0 ? static_cast(packages) : 1; +} + +int CPUInfo::get_cpu_cores() { + int ncpu = static_cast(Platform::sysctlLong("hw.ncpu")); + + std::string result = Platform::exec("sysctl -n kern.smp.cpus 2>/dev/null"); + if (!result.empty()) { + try { + int cores = std::stoi(Platform::trim(result)); + if (cores > 0) return cores; + } catch (...) {} + } + + return ncpu > 0 ? ncpu : 1; +} + +int CPUInfo::get_cpu_logical_processors() { + int ncpu = static_cast(Platform::sysctlLong("hw.ncpu")); + return ncpu > 0 ? ncpu : 1; +} + +std::string CPUInfo::get_cpu_virtualization() { + std::string features = Platform::exec("sysctl -n hw.vmm.vmx.cap.guest 2>/dev/null"); + if (!features.empty() && Platform::trim(features) != "0") { + return "VT-x Enabled"; + } + + features = Platform::exec("sysctl -n hw.vmm.svm.features 2>/dev/null"); + if (!features.empty() && Platform::trim(features) != "0") { + return "AMD-V Enabled"; + } + + return "Disabled"; +} + +std::string CPUInfo::get_cpu_l1_cache() { + std::string result = Platform::exec("sysctl -n hw.cacheconfig 2>/dev/null | awk '{print $2}'"); + result = Platform::trim(result); + if (!result.empty()) { + try { + int kb = std::stoi(result) / 1024; + return std::to_string(kb) + " KB"; + } catch (...) {} + } + + result = Platform::exec("dmesg | grep -i 'L1 cache' | head -1"); + if (!result.empty()) { + return Platform::trim(result); + } + + return "N/A"; +} + +std::string CPUInfo::get_cpu_l2_cache() { + std::string result = Platform::exec("sysctl -n hw.cacheconfig 2>/dev/null | awk '{print $3}'"); + result = Platform::trim(result); + if (!result.empty()) { + try { + int size = std::stoi(result); + if (size >= 1024 * 1024) { + return std::to_string(size / (1024 * 1024)) + " MB"; + } + return std::to_string(size / 1024) + " KB"; + } catch (...) {} + } + return "N/A"; +} + +std::string CPUInfo::get_cpu_l3_cache() { + std::string result = Platform::exec("sysctl -n hw.cacheconfig 2>/dev/null | awk '{print $4}'"); + result = Platform::trim(result); + if (!result.empty()) { + try { + int size = std::stoi(result); + if (size >= 1024 * 1024) { + return std::to_string(size / (1024 * 1024)) + " MB"; + } + return std::to_string(size / 1024) + " KB"; + } catch (...) {} + } + return "N/A"; +} + +std::string CPUInfo::get_system_uptime() { + struct timeval boottime; + size_t len = sizeof(boottime); + int mib[2] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(mib, 2, &boottime, &len, nullptr, 0) == 0) { + time_t now = time(nullptr); + time_t uptime_seconds = now - boottime.tv_sec; + + int days = static_cast(uptime_seconds / 86400); + int hours = static_cast((uptime_seconds % 86400) / 3600); + int minutes = static_cast((uptime_seconds % 3600) / 60); + int seconds = static_cast(uptime_seconds % 60); + + std::ostringstream ss; + ss << days << ":" + << std::setw(2) << std::setfill('0') << hours << ":" + << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds; + return ss.str(); + } + return "Unknown"; +} + +int CPUInfo::get_process_count() { + std::string result = Platform::exec("ps ax 2>/dev/null | wc -l"); + result = Platform::trim(result); + if (!result.empty()) { + try { + int count = std::stoi(result); + return count > 0 ? count - 1 : 0; + } catch (...) {} + } + return 0; +} + +int CPUInfo::get_thread_count() { + std::string result = Platform::exec("ps -axH 2>/dev/null | wc -l"); + result = Platform::trim(result); + if (!result.empty()) { + try { + int count = std::stoi(result); + return count > 0 ? count - 1 : 0; + } catch (...) {} + } + return 0; +} + +int CPUInfo::get_handle_count() { + std::string result = Platform::exec("sysctl -n kern.openfiles 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty()) { + try { return std::stoi(result); } + catch (...) {} + } + return 0; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/DisplayInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/DisplayInfoFreeBSD.cpp new file mode 100644 index 0000000..9d13928 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/DisplayInfoFreeBSD.cpp @@ -0,0 +1,206 @@ +#include "../Platform.h" +#include "../../DisplayInfo.h" +#include +#include +#include +#include +#include + +std::string DisplayInfo::WideToUtf8(const wchar_t*) { + return ""; +} + +std::string DisplayInfo::scaleMultiplier(int scalePercent) { + float mul = scalePercent / 100.0f; + char buf[32]; + if (fabsf(mul - roundf(mul)) < 0.001f) { + snprintf(buf, sizeof(buf), "%.0fx", mul); + } else { + snprintf(buf, sizeof(buf), "%.2fx", mul); + } + return std::string(buf); +} + +int DisplayInfo::computeUpscaleFactor(int currentWidth, int nativeWidth) { + if (nativeWidth <= 0 || currentWidth <= 0) return 1; + float ratio = static_cast(currentWidth) / static_cast(nativeWidth); + if (ratio < 1.25f) return 1; + return static_cast(std::round(ratio)); +} + +std::string DisplayInfo::computeAspectRatio(int w, int h) { + if (w <= 0 || h <= 0) return "Unknown"; + int a = w, b = h; + while (b != 0) { int t = b; b = a % b; a = t; } + return std::to_string(w/a) + ":" + std::to_string(h/a); +} + +bool DisplayInfo::isNvidiaPresent() { + return Platform::fileExists("/dev/nvidia0"); +} + +bool DisplayInfo::isAMDPresent() { + return Platform::fileExists("/dev/dri/card0"); +} + +DisplayInfo::EDIDInfo DisplayInfo::parseEDID(const unsigned char* edid, size_t size) { + EDIDInfo info = {"", 0, 0, false}; + if (!edid || size < 128) return info; + if (edid[0] != 0x00 || edid[1] != 0xFF || edid[7] != 0x00) return info; + + if (size >= 72) { + unsigned short hActive = ((edid[58] >> 4) << 8) | edid[56]; + unsigned short vActive = ((edid[61] >> 4) << 8) | edid[59]; + if (hActive > 0 && vActive > 0) { + info.nativeWidth = hActive; + info.nativeHeight = vActive; + info.valid = true; + } + } + + for (int i = 54; i < 126; i += 18) { + if (i + 17 >= static_cast(size)) break; + if (edid[i] == 0x00 && edid[i + 1] == 0x00 && edid[i + 3] == 0xFC) { + std::string name; + for (int j = 5; j < 18; ++j) { + if (edid[i + j] == 0x0A || edid[i + j] == 0x00) break; + if (edid[i + j] >= 0x20 && edid[i + j] <= 0x7E) { + name += static_cast(edid[i + j]); + } + } + while (!name.empty() && name.back() == ' ') name.pop_back(); + if (!name.empty()) info.friendlyName = name; + break; + } + } + return info; +} + +std::string DisplayInfo::getFriendlyNameFromEDID(const std::wstring&) { + DIR* dir = opendir("/sys/class/drm"); + if (!dir) return "Generic Monitor"; + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") != std::string::npos) { + std::string edidPath = "/sys/class/drm/" + name + "/edid"; + std::ifstream file(edidPath, std::ios::binary); + if (file.is_open()) { + std::vector data((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + if (data.size() >= 128) { + EDIDInfo info = parseEDID(data.data(), data.size()); + if (!info.friendlyName.empty()) { + closedir(dir); + return info.friendlyName; + } + } + } + } + } + closedir(dir); + return "Generic Monitor"; +} + +DisplayInfo::DisplayInfo() { + refresh(); +} + +bool DisplayInfo::refresh() { + screens.clear(); + return populateFromDXGI(); +} + +const std::vector& DisplayInfo::getScreens() const { + return screens; +} + +bool DisplayInfo::populateFromDXGI() { + std::string displayEnv = Platform::getEnv("DISPLAY"); + std::string waylandEnv = Platform::getEnv("WAYLAND_DISPLAY"); + + if (displayEnv.empty() && waylandEnv.empty()) { + return true; + } + + std::string xrandr = Platform::exec("xrandr --current 2>/dev/null"); + if (xrandr.empty()) return true; + + std::istringstream iss(xrandr); + std::string line; + ScreenInfo current; + bool inMonitor = false; + + while (std::getline(iss, line)) { + if (line.find(" connected") != std::string::npos) { + if (inMonitor && current.current_width > 0) { + screens.push_back(current); + } + + current = ScreenInfo(); + inMonitor = true; + + size_t spacePos = line.find(' '); + if (spacePos != std::string::npos) { + current.name = line.substr(0, spacePos); + } + + size_t resStart = line.find_first_of("0123456789"); + if (resStart != std::string::npos) { + int w = 0, h = 0, offX = 0, offY = 0; + if (sscanf(line.c_str() + resStart, "%dx%d+%d+%d", &w, &h, &offX, &offY) >= 2) { + current.current_width = w; + current.current_height = h; + current.native_width = w; + current.native_height = h; + current.native_resolution = std::to_string(w) + "x" + std::to_string(h); + current.aspect_ratio = computeAspectRatio(w, h); + } + } + } + else if (inMonitor && line.find("*") != std::string::npos) { + size_t hzStart = line.find_last_of("0123456789."); + if (hzStart != std::string::npos) { + size_t hzEnd = line.find("*"); + if (hzEnd != std::string::npos) { + while (hzStart > 0 && (std::isdigit(line[hzStart-1]) || line[hzStart-1] == '.')) { + hzStart--; + } + std::string hzStr = line.substr(hzStart, hzEnd - hzStart); + try { + current.refresh_rate = static_cast(std::stof(Platform::trim(hzStr))); + } catch (...) { + current.refresh_rate = 60; + } + } + } + } + } + + if (inMonitor && current.current_width > 0) { + screens.push_back(current); + } + + std::string dpiStr = Platform::exec("xrdb -query 2>/dev/null | grep -i dpi | head -1 | awk '{print $2}'"); + int dpi = 96; + if (!dpiStr.empty()) { + try { dpi = std::stoi(Platform::trim(dpiStr)); } + catch (...) { dpi = 96; } + } + + for (auto& screen : screens) { + screen.scale_percent = static_cast(std::round((dpi / 96.0f) * 100.0f)); + screen.scale_mul = scaleMultiplier(screen.scale_percent); + screen.upscale = "1x"; + screen.dsr_enabled = false; + screen.dsr_type = "None"; + } + + return !screens.empty(); +} + +bool DisplayInfo::enrichWithNVAPI() { return true; } +bool DisplayInfo::enrichWithADL() { return true; } diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/GPUInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/GPUInfoFreeBSD.cpp new file mode 100644 index 0000000..0f4a55f --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/GPUInfoFreeBSD.cpp @@ -0,0 +1,145 @@ +#include "../Platform.h" +#include "../../GPUInfo.h" +#include +#include +#include + +static std::string getVendorFromPciConf(const std::string& line) { + if (line.find("NVIDIA") != std::string::npos || line.find("nvidia") != std::string::npos) return "NVIDIA"; + if (line.find("AMD") != std::string::npos || line.find("ATI") != std::string::npos || + line.find("Radeon") != std::string::npos) return "AMD"; + if (line.find("Intel") != std::string::npos) return "Intel"; + return "Unknown"; +} + +float GPUInfo::get_gpu_usage() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null"); + if (!result.empty()) { + try { return std::stof(Platform::trim(result)); } + catch (...) {} + } + } + + std::string result = Platform::exec("sysctl -n dev.drm.0.hwmon.temp 2>/dev/null"); + if (!result.empty()) { + return -1.0f; + } + + return -1.0f; +} + +float GPUInfo::get_gpu_temperature() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null"); + if (!result.empty()) { + try { return std::stof(Platform::trim(result)); } + catch (...) {} + } + } + + std::string result = Platform::exec("sysctl -n hw.acpi.thermal.tz0.temperature 2>/dev/null"); + if (!result.empty()) { + try { + std::string temp = Platform::trim(result); + size_t cPos = temp.find('C'); + if (cPos != std::string::npos) { + temp = temp.substr(0, cPos); + } + return std::stof(temp); + } catch (...) {} + } + + return -1.0f; +} + +int GPUInfo::get_gpu_core_count() { + return 0; +} + +std::vector GPUInfo::get_all_gpu_info() { + std::vector list; + + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=name,memory.total,driver_version,utilization.gpu,temperature.gpu,clocks.gr --format=csv,noheader,nounits 2>/dev/null"); + + std::istringstream iss(result); + std::string line; + while (std::getline(iss, line)) { + if (line.empty()) continue; + + auto parts = Platform::split(line, ','); + if (parts.size() >= 6) { + gpu_data d; + d.gpu_name = Platform::trim(parts[0]); + + float memMB = std::stof(Platform::trim(parts[1])); + std::ostringstream memStream; + memStream << std::fixed << std::setprecision(1) << (memMB / 1024.0f) << " GB"; + d.gpu_memory = memStream.str(); + + d.gpu_driver_version = Platform::trim(parts[2]); + d.gpu_vendor = "NVIDIA"; + d.gpu_usage = std::stof(Platform::trim(parts[3])); + d.gpu_temperature = std::stof(Platform::trim(parts[4])); + d.gpu_frequency = std::stof(Platform::trim(parts[5])); + d.gpu_core_count = 0; + + list.push_back(d); + } + } + } + + if (list.empty()) { + std::string pciconf = Platform::exec("pciconf -lv 2>/dev/null | grep -B4 -E 'display|VGA|3D'"); + + std::istringstream iss(pciconf); + std::string line; + gpu_data current; + bool inGPU = false; + + while (std::getline(iss, line)) { + if (line.find("display") != std::string::npos || + line.find("VGA") != std::string::npos || + line.find("3D") != std::string::npos) { + if (inGPU && !current.gpu_name.empty()) { + list.push_back(current); + } + current = gpu_data(); + inGPU = true; + } + + if (inGPU) { + if (line.find("device") != std::string::npos && line.find("=") != std::string::npos) { + size_t eq = line.find("="); + if (eq != std::string::npos) { + std::string name = line.substr(eq + 1); + name.erase(std::remove(name.begin(), name.end(), '\''), name.end()); + current.gpu_name = Platform::trim(name); + current.gpu_vendor = getVendorFromPciConf(name); + } + } + if (line.find("vendor") != std::string::npos && line.find("=") != std::string::npos) { + size_t eq = line.find("="); + if (eq != std::string::npos) { + std::string vendor = line.substr(eq + 1); + vendor.erase(std::remove(vendor.begin(), vendor.end(), '\''), vendor.end()); + current.gpu_vendor = getVendorFromPciConf(vendor); + } + } + } + } + + if (inGPU && !current.gpu_name.empty()) { + current.gpu_memory = "Unknown"; + current.gpu_driver_version = "Unknown"; + current.gpu_usage = get_gpu_usage(); + current.gpu_temperature = get_gpu_temperature(); + current.gpu_frequency = -1.0f; + current.gpu_core_count = 0; + list.push_back(current); + } + } + + return list; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/MemoryInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/MemoryInfoFreeBSD.cpp new file mode 100644 index 0000000..6dffe6a --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/MemoryInfoFreeBSD.cpp @@ -0,0 +1,99 @@ +#include "../Platform.h" +#include "../../MemoryInfo.h" +#include +#include +#include + +MemoryInfo::MemoryInfo() { + fetchSystemMemory(); + fetchModulesInfo(); +} + +void MemoryInfo::fetchSystemMemory() { + unsigned long physmem = Platform::sysctlULong("hw.physmem"); + + unsigned long pagesize = Platform::sysctlULong("hw.pagesize"); + if (pagesize == 0) pagesize = 4096; + + unsigned long free_count = Platform::sysctlULong("vm.stats.vm.v_free_count"); + unsigned long inactive = Platform::sysctlULong("vm.stats.vm.v_inactive_count"); + unsigned long cache = Platform::sysctlULong("vm.stats.vm.v_cache_count"); + + unsigned long available = (free_count + inactive + cache) * pagesize; + + totalGB = static_cast((physmem + 1024UL * 1024UL * 1024UL - 1) / (1024UL * 1024UL * 1024UL)); + freeGB = static_cast(available / (1024UL * 1024UL * 1024UL)); +} + +void MemoryInfo::fetchModulesInfo() { + modules.clear(); + + std::string output = Platform::exec("dmidecode -t memory 2>/dev/null | grep -E 'Size:|Speed:|Type:' | head -20"); + + if (output.empty()) { + MemoryModule mod; + mod.capacity = std::to_string(totalGB) + "GB"; + mod.type = "Unknown"; + mod.speed = "Unknown"; + modules.push_back(mod); + return; + } + + std::istringstream iss(output); + std::string line; + MemoryModule current; + + while (std::getline(iss, line)) { + line = Platform::trim(line); + + if (line.find("Size:") == 0) { + if (!current.capacity.empty() && current.capacity.find("No Module") == std::string::npos) { + modules.push_back(current); + } + current = MemoryModule(); + + std::string size = line.substr(5); + size = Platform::trim(size); + if (size.find("No Module") == std::string::npos) { + current.capacity = size; + } + } + else if (line.find("Type:") == 0 && current.type.empty()) { + std::string type = Platform::trim(line.substr(5)); + if (type != "Unknown" && type != "Other") { + current.type = type; + } + } + else if (line.find("Speed:") == 0 && current.speed.empty()) { + std::string speed = Platform::trim(line.substr(6)); + if (speed != "Unknown") { + current.speed = speed; + } + } + } + + if (!current.capacity.empty() && current.capacity.find("No Module") == std::string::npos) { + modules.push_back(current); + } + + if (modules.empty()) { + MemoryModule mod; + mod.capacity = std::to_string(totalGB) + "GB"; + mod.type = "Unknown"; + mod.speed = "Unknown"; + modules.push_back(mod); + } +} + +int MemoryInfo::getTotal() const { return totalGB; } +int MemoryInfo::getFree() const { return freeGB; } + +int MemoryInfo::getUsedPercentage() const { + if (totalGB == 0) return 0; + double percentage = (static_cast(totalGB - freeGB) / totalGB) * 100; + if (percentage > 100.0) percentage = 100.0; + if (percentage < 0.0) percentage = 0.0; + return static_cast(percentage); +} + +const std::vector& MemoryInfo::getModules() const { return modules; } diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/NetworkInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/NetworkInfoFreeBSD.cpp new file mode 100644 index 0000000..c27c923 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/NetworkInfoFreeBSD.cpp @@ -0,0 +1,154 @@ +#include "../Platform.h" +#include "../HttpClient.h" +#include "../../NetworkInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::string getPrimaryInterface() { + struct ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) return ""; + + std::string primary; + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + + std::string name = ifa->ifa_name; + if (name == "lo0") continue; + if (name.find("bridge") == 0) continue; + if (name.find("tap") == 0) continue; + if (name.find("tun") == 0) continue; + + if (ifa->ifa_flags & IFF_UP) { + primary = name; + break; + } + } + + freeifaddrs(ifaddr); + return primary; +} + +std::string NetworkInfo::get_local_ip() { + struct ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) return "Unknown"; + + std::string result = "Unknown"; + + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + + std::string name = ifa->ifa_name; + if (name == "lo0") continue; + + if (!(ifa->ifa_flags & IFF_UP)) continue; + + char ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr, ip, sizeof(ip)); + + int prefix = 24; + if (ifa->ifa_netmask) { + uint32_t mask = ntohl(((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr); + prefix = 0; + while (mask) { + prefix += (mask & 1); + mask >>= 1; + } + } + + std::ostringstream oss; + oss << ip << "/" << prefix; + result = oss.str(); + break; + } + + freeifaddrs(ifaddr); + return result; +} + +std::string NetworkInfo::get_mac_address() { + struct ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) return "Unknown"; + + std::string primary = getPrimaryInterface(); + std::string result = "Unknown"; + + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_LINK) continue; + + std::string name = ifa->ifa_name; + if (!primary.empty() && name != primary) continue; + if (name == "lo0") continue; + + struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifa->ifa_addr; + if (sdl->sdl_alen == 6) { + unsigned char* mac = (unsigned char*)LLADDR(sdl); + char buf[18]; + snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + result = buf; + break; + } + } + + freeifaddrs(ifaddr); + return result; +} + +std::string NetworkInfo::get_locale() { + std::string locale = Platform::getEnv("LANG"); + if (!locale.empty()) { + size_t dot = locale.find('.'); + if (dot != std::string::npos) { + locale = locale.substr(0, dot); + } + std::replace(locale.begin(), locale.end(), '_', '-'); + return locale; + } + + return "en-US"; +} + +std::string NetworkInfo::get_network_name() { + if (Platform::commandExists("ifconfig")) { + std::string result = Platform::exec("ifconfig wlan0 2>/dev/null | grep 'ssid' | awk '{print $2}'"); + result = Platform::trim(result); + if (!result.empty()) return result; + } + + std::string iface = getPrimaryInterface(); + return iface.empty() ? "Unknown" : iface; +} + +std::string NetworkInfo::get_public_ip() { + Platform::HttpClient::Response resp = Platform::HttpClient::get("api.ipify.org", "/", 80, 5000); + if (resp.success) { + return Platform::trim(resp.body); + } + + resp = Platform::HttpClient::get("ifconfig.me", "/ip", 80, 5000); + if (resp.success) { + return Platform::trim(resp.body); + } + + return "Unknown"; +} + +std::string NetworkInfo::get_network_download_speed() { + return Platform::HttpClient::downloadSpeed("speed.cloudflare.com", "/__down", 1000000, 5000); +} + +std::string NetworkInfo::get_network_upload_speed() { + return Platform::HttpClient::uploadSpeed("speed.cloudflare.com", "/__up", 500000, 5000); +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/OSInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/OSInfoFreeBSD.cpp new file mode 100644 index 0000000..070979d --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/OSInfoFreeBSD.cpp @@ -0,0 +1,105 @@ +#include "../Platform.h" +#include "../../OSInfo.h" +#include +#include +#include +#include +#include +#include +#include + +std::string OSInfo::GetOSVersion() { + std::string release = Platform::sysctlString("kern.osrelease"); + if (!release.empty()) { + size_t dash = release.find('-'); + if (dash != std::string::npos) { + return release.substr(0, dash); + } + return release; + } + return "Unknown"; +} + +std::string OSInfo::GetOSArchitecture() { + std::string arch = Platform::sysctlString("hw.machine_arch"); + if (!arch.empty()) { + if (arch == "amd64" || arch == "x86_64") return "64-bit"; + if (arch == "i386" || arch == "i686") return "32-bit"; + if (arch == "aarch64" || arch == "arm64") return "ARM64"; + if (arch == "armv7" || arch == "armv6") return "ARM32"; + return arch; + } + return "Unknown"; +} + +std::string OSInfo::GetOSName() { + std::string ostype = Platform::sysctlString("kern.ostype"); + std::string version = GetOSVersion(); + + if (!ostype.empty()) { + if (!version.empty() && version != "Unknown") { + return ostype + " " + version; + } + return ostype; + } + return "FreeBSD"; +} + +std::string OSInfo::get_os_install_date() { + struct stat st; + if (stat("/", &st) == 0) { + char buf[64]; + struct tm* tm_info = localtime(&st.st_ctime); + strftime(buf, sizeof(buf), "%Y-%m-%d", tm_info); + return std::string(buf); + } + return "N/A"; +} + +std::string OSInfo::get_os_serial_number() { + std::string result = Platform::exec("kenv smbios.system.serial 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty() && result != "To Be Filled By O.E.M." && result != "None") { + return result; + } + + result = Platform::exec("kenv smbios.planar.serial 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty() && result != "To Be Filled By O.E.M." && result != "None") { + return result; + } + + return "N/A (requires root)"; +} + +std::string OSInfo::get_os_uptime() { + struct timeval boottime; + size_t len = sizeof(boottime); + int mib[2] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(mib, 2, &boottime, &len, nullptr, 0) == 0) { + time_t now = time(nullptr); + time_t uptime_seconds = now - boottime.tv_sec; + + int days = static_cast(uptime_seconds / 86400); + int hours = static_cast((uptime_seconds % 86400) / 3600); + int minutes = static_cast((uptime_seconds % 3600) / 60); + int seconds = static_cast(uptime_seconds % 60); + + std::ostringstream ss; + ss << days << ":" + << std::setw(2) << std::setfill('0') << hours << ":" + << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds; + return ss.str(); + } + return "Unknown"; +} + +std::string OSInfo::get_os_kernel_info() { + struct utsname buf; + if (uname(&buf) == 0) { + return std::string(buf.sysname) + " " + buf.release; + } + return "FreeBSD"; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/PerformanceInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/PerformanceInfoFreeBSD.cpp new file mode 100644 index 0000000..c94e4cf --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/PerformanceInfoFreeBSD.cpp @@ -0,0 +1,127 @@ +#include "../Platform.h" +#include "../../PerformanceInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct PerformanceInfo::Impl { + long prev_cp_time[5] = {0}; + bool first_call = true; +}; + +PerformanceInfo::PerformanceInfo() { + pImpl = new Impl(); +} + +PerformanceInfo::~PerformanceInfo() { + delete pImpl; + pImpl = nullptr; +} + +std::string PerformanceInfo::format_uptime(unsigned long long totalMilliseconds) { + unsigned long long totalSeconds = totalMilliseconds / 1000ULL; + int hours = static_cast(totalSeconds / 3600ULL); + int minutes = static_cast((totalSeconds % 3600ULL) / 60ULL); + int seconds = static_cast(totalSeconds % 60ULL); + return std::to_string(hours) + "h " + std::to_string(minutes) + "m " + std::to_string(seconds) + "s"; +} + +std::string PerformanceInfo::get_system_uptime() { + struct timeval boottime; + size_t len = sizeof(boottime); + int mib[2] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(mib, 2, &boottime, &len, nullptr, 0) == 0) { + time_t now = time(nullptr); + time_t uptime_seconds = now - boottime.tv_sec; + unsigned long long ms = static_cast(uptime_seconds * 1000); + return format_uptime(ms); + } + return "Unknown"; +} + +float PerformanceInfo::get_cpu_usage_percent() { + long cp_time[5]; + size_t len = sizeof(cp_time); + + if (sysctlbyname("kern.cp_time", cp_time, &len, nullptr, 0) != 0) { + return 0.0f; + } + + if (pImpl->first_call) { + memcpy(pImpl->prev_cp_time, cp_time, sizeof(cp_time)); + pImpl->first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return get_cpu_usage_percent(); + } + + long total_delta = 0; + long idle_delta = 0; + + for (int i = 0; i < 5; i++) { + total_delta += cp_time[i] - pImpl->prev_cp_time[i]; + } + idle_delta = cp_time[4] - pImpl->prev_cp_time[4]; + + memcpy(pImpl->prev_cp_time, cp_time, sizeof(cp_time)); + + if (total_delta == 0) return 0.0f; + + return (1.0f - (float)idle_delta / (float)total_delta) * 100.0f; +} + +float PerformanceInfo::get_ram_usage_percent() { + unsigned long physmem = Platform::sysctlULong("hw.physmem"); + unsigned long pagesize = Platform::sysctlULong("hw.pagesize"); + if (pagesize == 0) pagesize = 4096; + + unsigned long free_count = Platform::sysctlULong("vm.stats.vm.v_free_count"); + unsigned long inactive = Platform::sysctlULong("vm.stats.vm.v_inactive_count"); + unsigned long cache = Platform::sysctlULong("vm.stats.vm.v_cache_count"); + + unsigned long available = (free_count + inactive + cache) * pagesize; + + if (physmem == 0) return 0.0f; + + unsigned long used = physmem - available; + return static_cast((used * 100.0) / physmem); +} + +float PerformanceInfo::get_disk_usage_percent() { + struct statfs* mntbuf; + int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + + if (mntsize <= 0) return 0.0f; + + for (int i = 0; i < mntsize; i++) { + if (std::string(mntbuf[i].f_mntonname) == "/") { + unsigned long long total = (unsigned long long)mntbuf[i].f_blocks * mntbuf[i].f_bsize; + unsigned long long free = (unsigned long long)mntbuf[i].f_bfree * mntbuf[i].f_bsize; + + if (total == 0) return 0.0f; + + unsigned long long used = total - free; + return static_cast((used * 100.0) / total); + } + } + + return 0.0f; +} + +float PerformanceInfo::get_gpu_usage_percent() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null"); + if (!result.empty()) { + try { return std::stof(Platform::trim(result)); } + catch (...) {} + } + } + + return 0.0f; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/StorageInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/StorageInfoFreeBSD.cpp new file mode 100644 index 0000000..d2beb2e --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/StorageInfoFreeBSD.cpp @@ -0,0 +1,175 @@ +#include "../Platform.h" +#include "../../StorageInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::set pseudoFS = {"devfs", "fdescfs", "procfs", "linprocfs", + "linsysfs", "tmpfs", "nullfs", "mqueuefs"}; + +std::string StorageInfo::get_storage_type(const std::string&, const std::string& root_path, bool) { + std::string geom = Platform::exec("geom disk list 2>/dev/null"); + + if (geom.find("rotationrate: 0") != std::string::npos) { + return "SSD"; + } + + std::string camctl = Platform::exec("camcontrol identify da0 2>/dev/null | grep -i 'rotation rate'"); + if (!camctl.empty()) { + if (camctl.find("non-rotating") != std::string::npos || + camctl.find("Solid State") != std::string::npos) { + return "SSD"; + } + return "HDD"; + } + + if (root_path.find("nvme") != std::string::npos || + root_path.find("nvd") != std::string::npos) { + return "SSD"; + } + + if (root_path.find("da") != std::string::npos) { + std::string usbconf = Platform::exec("usbconfig list 2>/dev/null"); + if (!usbconf.empty() && usbconf.find("DISK") != std::string::npos) { + return "USB"; + } + } + + return "Unknown"; +} + +static double measureSpeed(const std::string& path, bool write) { + const size_t BUF_SIZE = 16 * 1024 * 1024; + std::vector buffer(BUF_SIZE, 'X'); + + std::string testFile = path + "/.binaryfetch_speed_test"; + + if (write) { + auto start = std::chrono::high_resolution_clock::now(); + int fd = open(testFile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644); + if (fd < 0) return 0.0; + + ssize_t written = ::write(fd, buffer.data(), BUF_SIZE); + fsync(fd); + close(fd); + + auto end = std::chrono::high_resolution_clock::now(); + + if (written <= 0) { + unlink(testFile.c_str()); + return 0.0; + } + + double seconds = std::chrono::duration(end - start).count(); + if (seconds < 0.001) seconds = 0.001; + + return (written / (1024.0 * 1024.0)) / seconds; + } else { + int fd = open(testFile.c_str(), O_RDONLY); + if (fd < 0) return 0.0; + + auto start = std::chrono::high_resolution_clock::now(); + ssize_t bytesRead = read(fd, buffer.data(), BUF_SIZE); + close(fd); + unlink(testFile.c_str()); + + auto end = std::chrono::high_resolution_clock::now(); + + if (bytesRead <= 0) return 0.0; + + double seconds = std::chrono::duration(end - start).count(); + if (seconds < 0.001) seconds = 0.001; + + return (bytesRead / (1024.0 * 1024.0)) / seconds; + } +} + +std::vector StorageInfo::get_all_storage_info() { + std::vector all_disks; + + struct statfs* mntbuf; + int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + + if (mntsize <= 0) return all_disks; + + std::set seen; + + for (int i = 0; i < mntsize; i++) { + std::string fstype = mntbuf[i].f_fstypename; + std::string device = mntbuf[i].f_mntfromname; + std::string mountpoint = mntbuf[i].f_mntonname; + + if (pseudoFS.count(fstype) || device.find("/dev/") != 0) continue; + if (seen.count(device)) continue; + seen.insert(device); + + unsigned long long total = (unsigned long long)mntbuf[i].f_blocks * mntbuf[i].f_bsize; + unsigned long long free = (unsigned long long)mntbuf[i].f_bfree * mntbuf[i].f_bsize; + unsigned long long used = total - free; + + if (total < 100 * 1024 * 1024) continue; + + double totalGiB = total / (1024.0 * 1024.0 * 1024.0); + double usedGiB = used / (1024.0 * 1024.0 * 1024.0); + double usedPercent = (total > 0) ? (used * 100.0 / total) : 0.0; + + storage_data disk; + disk.drive_letter = "Disk (" + mountpoint + ")"; + + std::ostringstream usedStr, totalStr, percentStr; + usedStr << std::fixed << std::setprecision(2) << usedGiB; + totalStr << std::fixed << std::setprecision(2) << totalGiB; + percentStr << "(" << static_cast(usedPercent) << "%)"; + + disk.used_space = usedStr.str(); + disk.total_space = totalStr.str(); + disk.used_percentage = percentStr.str(); + disk.file_system = fstype; + disk.storage_type = get_storage_type("", mountpoint, false); + disk.is_external = (disk.storage_type == "USB"); + + double w = measureSpeed(mountpoint, true); + usleep(100000); + double r = measureSpeed(mountpoint, false); + + std::ostringstream readStr, writeStr; + readStr << std::fixed << std::setprecision(2) << r; + writeStr << std::fixed << std::setprecision(2) << w; + disk.read_speed = readStr.str(); + disk.write_speed = writeStr.str(); + + if (disk.storage_type == "USB") { + disk.predicted_read_speed = "100"; + disk.predicted_write_speed = "80"; + } else if (disk.storage_type == "SSD") { + disk.predicted_read_speed = "500"; + disk.predicted_write_speed = "450"; + } else if (disk.storage_type == "HDD") { + disk.predicted_read_speed = "140"; + disk.predicted_write_speed = "120"; + } else { + disk.predicted_read_speed = "---"; + disk.predicted_write_speed = "---"; + } + + disk.serial_number = "N/A"; + + all_disks.push_back(disk); + } + + return all_disks; +} + +void StorageInfo::process_storage_info(std::function callback) { + auto disks = get_all_storage_info(); + for (const auto& disk : disks) { + callback(disk); + } +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/freebsd/SystemInfoFreeBSD.cpp b/project_binary_fetch/binary_fetch_v1/platform/freebsd/SystemInfoFreeBSD.cpp new file mode 100644 index 0000000..d5cab86 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/freebsd/SystemInfoFreeBSD.cpp @@ -0,0 +1,58 @@ +#include "../Platform.h" +#include "../../SystemInfo.h" +#include + +static std::string readKenv(const std::string& key) { + std::string cmd = "kenv " + key + " 2>/dev/null"; + std::string value = Platform::exec(cmd); + value = Platform::trim(value); + + if (value.empty() || value == "To Be Filled By O.E.M." || + value == "Default string" || value == "Not Specified" || + value == "None" || value.find("unknown") != std::string::npos) { + return "N/A"; + } + return value; +} + +std::string SystemInfo::get_bios_vendor() { + return readKenv("smbios.bios.vendor"); +} + +std::string SystemInfo::get_bios_version() { + return readKenv("smbios.bios.version"); +} + +std::string SystemInfo::get_bios_release_date() { + return readKenv("smbios.bios.reldate"); +} + +std::string SystemInfo::get_motherboard_manufacturer() { + return readKenv("smbios.planar.maker"); +} + +std::string SystemInfo::get_motherboard_model() { + return readKenv("smbios.planar.product"); +} + +std::vector>> SystemInfo::get_bios_info() { + std::vector>> info; + + info.push_back({"BIOS Vendor", {"", get_bios_vendor()}}); + info.push_back({"BIOS Version", {"", get_bios_version()}}); + info.push_back({"BIOS Date", {"", get_bios_release_date()}}); + info.push_back({"Board Manufacturer", {"", get_motherboard_manufacturer()}}); + info.push_back({"Board Model", {"", get_motherboard_model()}}); + + std::string product = readKenv("smbios.system.product"); + if (product != "N/A") { + info.push_back({"System Product", {"", product}}); + } + + std::string sys_vendor = readKenv("smbios.system.maker"); + if (sys_vendor != "N/A") { + info.push_back({"System Vendor", {"", sys_vendor}}); + } + + return info; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/CPUInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/CPUInfoLinux.cpp new file mode 100644 index 0000000..1970920 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/CPUInfoLinux.cpp @@ -0,0 +1,239 @@ +#include "../Platform.h" +#include "../../CPUInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static long prev_idle = 0; +static long prev_total = 0; +static bool first_call = true; + +std::string CPUInfo::get_cpu_info() { + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string model = Platform::parseValue(content, "model name"); + return model.empty() ? "Unknown CPU" : model; +} + +float CPUInfo::get_cpu_utilization() { + std::string stat = Platform::readFileLine("/proc/stat"); + if (stat.empty()) return 0.0f; + + long user, nice, system, idle, iowait, irq, softirq, steal; + sscanf(stat.c_str(), "cpu %ld %ld %ld %ld %ld %ld %ld %ld", + &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal); + + long idle_time = idle + iowait; + long total_time = user + nice + system + idle + iowait + irq + softirq + steal; + + if (first_call) { + prev_idle = idle_time; + prev_total = total_time; + first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return get_cpu_utilization(); + } + + long idle_delta = idle_time - prev_idle; + long total_delta = total_time - prev_total; + + prev_idle = idle_time; + prev_total = total_time; + + if (total_delta == 0) return 0.0f; + + return (1.0f - (float)idle_delta / (float)total_delta) * 100.0f; +} + +std::string CPUInfo::get_cpu_base_speed() { + std::string freq = Platform::readFileLine("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency"); + if (freq.empty()) { + freq = Platform::readFileLine("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"); + } + + if (!freq.empty()) { + float ghz = std::stof(freq) / 1000000.0f; + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << ghz << " GHz"; + return ss.str(); + } + + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string mhz = Platform::parseValue(content, "cpu MHz"); + if (!mhz.empty()) { + float ghz = std::stof(mhz) / 1000.0f; + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << ghz << " GHz"; + return ss.str(); + } + + return "N/A"; +} + +std::string CPUInfo::get_cpu_speed() { + std::string freq = Platform::readFileLine("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"); + + if (!freq.empty()) { + float ghz = std::stof(freq) / 1000000.0f; + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << ghz << " GHz"; + return ss.str(); + } + + return get_cpu_base_speed(); +} + +int CPUInfo::get_cpu_sockets() { + std::string content = Platform::readFile("/proc/cpuinfo"); + std::set physical_ids; + + std::istringstream iss(content); + std::string line; + while (std::getline(iss, line)) { + if (line.find("physical id") == 0) { + size_t pos = line.find(':'); + if (pos != std::string::npos) { + physical_ids.insert(Platform::trim(line.substr(pos + 1))); + } + } + } + + return physical_ids.empty() ? 1 : static_cast(physical_ids.size()); +} + +int CPUInfo::get_cpu_cores() { + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string cores = Platform::parseValue(content, "cpu cores"); + + if (!cores.empty()) { + return std::stoi(cores) * get_cpu_sockets(); + } + + std::string siblings = Platform::parseValue(content, "siblings"); + if (!siblings.empty()) { + return std::stoi(siblings) / 2; + } + + return get_cpu_logical_processors() / 2; +} + +int CPUInfo::get_cpu_logical_processors() { + int count = 0; + std::string content = Platform::readFile("/proc/cpuinfo"); + std::istringstream iss(content); + std::string line; + while (std::getline(iss, line)) { + if (line.find("processor") == 0) count++; + } + return count > 0 ? count : 1; +} + +std::string CPUInfo::get_cpu_virtualization() { + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string flags = Platform::parseValue(content, "flags"); + + if (flags.find("vmx") != std::string::npos) return "VT-x Enabled"; + if (flags.find("svm") != std::string::npos) return "AMD-V Enabled"; + + return "Disabled"; +} + +static std::string readCacheSize(int level) { + for (int i = 0; i < 4; i++) { + std::string path = "/sys/devices/system/cpu/cpu0/cache/index" + std::to_string(i) + "/"; + std::string levelStr = Platform::readFileLine(path + "level"); + if (levelStr.empty()) continue; + + if (std::stoi(levelStr) == level) { + std::string size = Platform::readFileLine(path + "size"); + if (!size.empty()) return Platform::trim(size); + } + } + return "N/A"; +} + +std::string CPUInfo::get_cpu_l1_cache() { + int total = 0; + for (int i = 0; i < 2; i++) { + std::string path = "/sys/devices/system/cpu/cpu0/cache/index" + std::to_string(i) + "/"; + std::string levelStr = Platform::readFileLine(path + "level"); + if (levelStr.empty() || std::stoi(levelStr) != 1) continue; + + std::string size = Platform::readFileLine(path + "size"); + if (!size.empty()) { + int val = std::stoi(size); + if (size.find('K') != std::string::npos) total += val; + else if (size.find('M') != std::string::npos) total += val * 1024; + } + } + + if (total > 0) { + std::ostringstream ss; + ss << total << " KB"; + return ss.str(); + } + return "N/A"; +} + +std::string CPUInfo::get_cpu_l2_cache() { return readCacheSize(2); } +std::string CPUInfo::get_cpu_l3_cache() { return readCacheSize(3); } + +std::string CPUInfo::get_system_uptime() { + std::string uptimeStr = Platform::readFileLine("/proc/uptime"); + if (uptimeStr.empty()) return "Unknown"; + + double uptime_seconds = std::stod(uptimeStr); + + int days = static_cast(uptime_seconds / 86400); + int hours = static_cast((uptime_seconds - days * 86400) / 3600); + int minutes = static_cast((uptime_seconds - days * 86400 - hours * 3600) / 60); + int seconds = static_cast(uptime_seconds) % 60; + + std::ostringstream ss; + ss << days << ":" + << std::setw(2) << std::setfill('0') << hours << ":" + << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds; + return ss.str(); +} + +int CPUInfo::get_process_count() { + DIR* dir = opendir("/proc"); + if (!dir) return 0; + + int count = 0; + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (entry->d_type == DT_DIR) { + bool is_pid = true; + for (const char* p = entry->d_name; *p; p++) { + if (*p < '0' || *p > '9') { is_pid = false; break; } + } + if (is_pid) count++; + } + } + closedir(dir); + return count; +} + +int CPUInfo::get_thread_count() { + std::string result = Platform::exec("ps -eo nlwp --no-headers 2>/dev/null | awk '{sum+=$1} END {print sum}'"); + if (!result.empty()) { + try { return std::stoi(Platform::trim(result)); } + catch (...) {} + } + return 0; +} + +int CPUInfo::get_handle_count() { + std::string result = Platform::exec("cat /proc/sys/fs/file-nr 2>/dev/null | awk '{print $1}'"); + if (!result.empty()) { + try { return std::stoi(Platform::trim(result)); } + catch (...) {} + } + return 0; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/DisplayInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/DisplayInfoLinux.cpp new file mode 100644 index 0000000..810f89a --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/DisplayInfoLinux.cpp @@ -0,0 +1,206 @@ +#include "../Platform.h" +#include "../../DisplayInfo.h" +#include +#include +#include +#include +#include + +std::string DisplayInfo::WideToUtf8(const wchar_t*) { + return ""; +} + +std::string DisplayInfo::scaleMultiplier(int scalePercent) { + float mul = scalePercent / 100.0f; + char buf[32]; + if (fabsf(mul - roundf(mul)) < 0.001f) { + snprintf(buf, sizeof(buf), "%.0fx", mul); + } else { + snprintf(buf, sizeof(buf), "%.2fx", mul); + } + return std::string(buf); +} + +int DisplayInfo::computeUpscaleFactor(int currentWidth, int nativeWidth) { + if (nativeWidth <= 0 || currentWidth <= 0) return 1; + float ratio = static_cast(currentWidth) / static_cast(nativeWidth); + if (ratio < 1.25f) return 1; + return static_cast(std::round(ratio)); +} + +std::string DisplayInfo::computeAspectRatio(int w, int h) { + if (w <= 0 || h <= 0) return "Unknown"; + int a = w, b = h; + while (b != 0) { int t = b; b = a % b; a = t; } + return std::to_string(w/a) + ":" + std::to_string(h/a); +} + +bool DisplayInfo::isNvidiaPresent() { + return Platform::fileExists("/sys/module/nvidia/version"); +} + +bool DisplayInfo::isAMDPresent() { + return Platform::fileExists("/sys/module/amdgpu/version"); +} + +DisplayInfo::EDIDInfo DisplayInfo::parseEDID(const unsigned char* edid, size_t size) { + EDIDInfo info = {"", 0, 0, false}; + if (!edid || size < 128) return info; + if (edid[0] != 0x00 || edid[1] != 0xFF || edid[7] != 0x00) return info; + + if (size >= 72) { + unsigned short hActive = ((edid[58] >> 4) << 8) | edid[56]; + unsigned short vActive = ((edid[61] >> 4) << 8) | edid[59]; + if (hActive > 0 && vActive > 0) { + info.nativeWidth = hActive; + info.nativeHeight = vActive; + info.valid = true; + } + } + + for (int i = 54; i < 126; i += 18) { + if (i + 17 >= static_cast(size)) break; + if (edid[i] == 0x00 && edid[i + 1] == 0x00 && edid[i + 3] == 0xFC) { + std::string name; + for (int j = 5; j < 18; ++j) { + if (edid[i + j] == 0x0A || edid[i + j] == 0x00) break; + if (edid[i + j] >= 0x20 && edid[i + j] <= 0x7E) { + name += static_cast(edid[i + j]); + } + } + while (!name.empty() && name.back() == ' ') name.pop_back(); + if (!name.empty()) info.friendlyName = name; + break; + } + } + return info; +} + +std::string DisplayInfo::getFriendlyNameFromEDID(const std::wstring&) { + DIR* dir = opendir("/sys/class/drm"); + if (!dir) return "Generic Monitor"; + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") != std::string::npos) { + std::string edidPath = "/sys/class/drm/" + name + "/edid"; + std::ifstream file(edidPath, std::ios::binary); + if (file.is_open()) { + std::vector data((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + if (data.size() >= 128) { + EDIDInfo info = parseEDID(data.data(), data.size()); + if (!info.friendlyName.empty()) { + closedir(dir); + return info.friendlyName; + } + } + } + } + } + closedir(dir); + return "Generic Monitor"; +} + +DisplayInfo::DisplayInfo() { + refresh(); +} + +bool DisplayInfo::refresh() { + screens.clear(); + return populateFromDXGI(); +} + +const std::vector& DisplayInfo::getScreens() const { + return screens; +} + +bool DisplayInfo::populateFromDXGI() { + std::string displayEnv = Platform::getEnv("DISPLAY"); + std::string waylandEnv = Platform::getEnv("WAYLAND_DISPLAY"); + + if (displayEnv.empty() && waylandEnv.empty()) { + return true; + } + + std::string xrandr = Platform::exec("xrandr --current 2>/dev/null"); + if (xrandr.empty()) return true; + + std::istringstream iss(xrandr); + std::string line; + ScreenInfo current; + bool inMonitor = false; + + while (std::getline(iss, line)) { + if (line.find(" connected") != std::string::npos) { + if (inMonitor && current.current_width > 0) { + screens.push_back(current); + } + + current = ScreenInfo(); + inMonitor = true; + + size_t spacePos = line.find(' '); + if (spacePos != std::string::npos) { + current.name = line.substr(0, spacePos); + } + + size_t resStart = line.find_first_of("0123456789"); + if (resStart != std::string::npos) { + int w = 0, h = 0, offX = 0, offY = 0; + if (sscanf(line.c_str() + resStart, "%dx%d+%d+%d", &w, &h, &offX, &offY) >= 2) { + current.current_width = w; + current.current_height = h; + current.native_width = w; + current.native_height = h; + current.native_resolution = std::to_string(w) + "x" + std::to_string(h); + current.aspect_ratio = computeAspectRatio(w, h); + } + } + } + else if (inMonitor && line.find("*") != std::string::npos) { + size_t hzStart = line.find_last_of("0123456789."); + if (hzStart != std::string::npos) { + size_t hzEnd = line.find("*"); + if (hzEnd != std::string::npos) { + while (hzStart > 0 && (std::isdigit(line[hzStart-1]) || line[hzStart-1] == '.')) { + hzStart--; + } + std::string hzStr = line.substr(hzStart, hzEnd - hzStart); + try { + current.refresh_rate = static_cast(std::stof(Platform::trim(hzStr))); + } catch (...) { + current.refresh_rate = 60; + } + } + } + } + } + + if (inMonitor && current.current_width > 0) { + screens.push_back(current); + } + + std::string dpiStr = Platform::exec("xrdb -query 2>/dev/null | grep -i dpi | head -1 | awk '{print $2}'"); + int dpi = 96; + if (!dpiStr.empty()) { + try { dpi = std::stoi(Platform::trim(dpiStr)); } + catch (...) { dpi = 96; } + } + + for (auto& screen : screens) { + screen.scale_percent = static_cast(std::round((dpi / 96.0f) * 100.0f)); + screen.scale_mul = scaleMultiplier(screen.scale_percent); + screen.upscale = "1x"; + screen.dsr_enabled = false; + screen.dsr_type = "None"; + } + + return !screens.empty(); +} + +bool DisplayInfo::enrichWithNVAPI() { return true; } +bool DisplayInfo::enrichWithADL() { return true; } diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/GPUInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/GPUInfoLinux.cpp new file mode 100644 index 0000000..e4381e5 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/GPUInfoLinux.cpp @@ -0,0 +1,159 @@ +#include "../Platform.h" +#include "../../GPUInfo.h" +#include +#include +#include +#include +#include + +static std::string findGPUInDRM() { + std::vector gpus; + DIR* dir = opendir("/sys/class/drm"); + if (!dir) return ""; + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") == std::string::npos) { + std::string vendorPath = "/sys/class/drm/" + name + "/device/vendor"; + std::string vendor = Platform::readFileLine(vendorPath); + if (!vendor.empty()) { + gpus.push_back(name); + } + } + } + closedir(dir); + return gpus.empty() ? "" : gpus[0]; +} + +float GPUInfo::get_gpu_usage() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null"); + if (!result.empty()) { + try { return std::stof(Platform::trim(result)); } + catch (...) {} + } + } + + std::string card = findGPUInDRM(); + if (!card.empty()) { + std::string busyPath = "/sys/class/drm/" + card + "/device/gpu_busy_percent"; + std::string busy = Platform::readFileLine(busyPath); + if (!busy.empty()) { + try { return std::stof(busy); } + catch (...) {} + } + } + + return -1.0f; +} + +float GPUInfo::get_gpu_temperature() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null"); + if (!result.empty()) { + try { return std::stof(Platform::trim(result)); } + catch (...) {} + } + } + + std::string card = findGPUInDRM(); + if (!card.empty()) { + DIR* hwmonDir = opendir(("/sys/class/drm/" + card + "/device/hwmon").c_str()); + if (hwmonDir) { + struct dirent* entry; + while ((entry = readdir(hwmonDir)) != nullptr) { + if (std::string(entry->d_name).find("hwmon") == 0) { + std::string tempPath = "/sys/class/drm/" + card + "/device/hwmon/" + entry->d_name + "/temp1_input"; + std::string temp = Platform::readFileLine(tempPath); + if (!temp.empty()) { + closedir(hwmonDir); + try { return std::stof(temp) / 1000.0f; } + catch (...) { return -1.0f; } + } + } + } + closedir(hwmonDir); + } + } + + return -1.0f; +} + +int GPUInfo::get_gpu_core_count() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=gpu_name --format=csv,noheader 2>/dev/null"); + return 0; + } + return 0; +} + +std::vector GPUInfo::get_all_gpu_info() { + std::vector list; + + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=name,memory.total,driver_version,utilization.gpu,temperature.gpu,clocks.gr --format=csv,noheader,nounits 2>/dev/null"); + + std::istringstream iss(result); + std::string line; + while (std::getline(iss, line)) { + if (line.empty()) continue; + + auto parts = Platform::split(line, ','); + if (parts.size() >= 6) { + gpu_data d; + d.gpu_name = Platform::trim(parts[0]); + + float memMB = std::stof(Platform::trim(parts[1])); + std::ostringstream memStream; + memStream << std::fixed << std::setprecision(1) << (memMB / 1024.0f) << " GB"; + d.gpu_memory = memStream.str(); + + d.gpu_driver_version = Platform::trim(parts[2]); + d.gpu_vendor = "NVIDIA"; + d.gpu_usage = std::stof(Platform::trim(parts[3])); + d.gpu_temperature = std::stof(Platform::trim(parts[4])); + d.gpu_frequency = std::stof(Platform::trim(parts[5])); + d.gpu_core_count = 0; + + list.push_back(d); + } + } + } + + std::string lspci = Platform::exec("lspci -nn 2>/dev/null | grep -i 'vga\\|3d\\|display'"); + if (!lspci.empty() && list.empty()) { + std::istringstream iss(lspci); + std::string line; + while (std::getline(iss, line)) { + gpu_data d; + + size_t colonPos = line.find(':'); + if (colonPos != std::string::npos && colonPos + 1 < line.length()) { + d.gpu_name = Platform::trim(line.substr(colonPos + 1)); + size_t bracketPos = d.gpu_name.find('['); + if (bracketPos != std::string::npos) { + d.gpu_name = Platform::trim(d.gpu_name.substr(0, bracketPos)); + } + } + + if (line.find("10de") != std::string::npos) d.gpu_vendor = "NVIDIA"; + else if (line.find("1002") != std::string::npos) d.gpu_vendor = "AMD"; + else if (line.find("8086") != std::string::npos) d.gpu_vendor = "Intel"; + else d.gpu_vendor = "Unknown"; + + d.gpu_memory = "Unknown"; + d.gpu_driver_version = "Unknown"; + d.gpu_usage = get_gpu_usage(); + d.gpu_temperature = get_gpu_temperature(); + d.gpu_frequency = -1.0f; + d.gpu_core_count = 0; + + if (!d.gpu_name.empty()) { + list.push_back(d); + } + } + } + + return list; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/MemoryInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/MemoryInfoLinux.cpp new file mode 100644 index 0000000..1de4a92 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/MemoryInfoLinux.cpp @@ -0,0 +1,106 @@ +#include "../Platform.h" +#include "../../MemoryInfo.h" +#include +#include +#include + +MemoryInfo::MemoryInfo() { + fetchSystemMemory(); + fetchModulesInfo(); +} + +void MemoryInfo::fetchSystemMemory() { + std::string content = Platform::readFile("/proc/meminfo"); + + auto parseKB = [&](const std::string& key) -> long long { + std::string val = Platform::parseValue(content, key); + if (val.empty()) return 0; + return std::stoll(val); + }; + + long long memTotal = parseKB("MemTotal"); + long long memAvailable = parseKB("MemAvailable"); + + if (memAvailable == 0) { + long long memFree = parseKB("MemFree"); + long long buffers = parseKB("Buffers"); + long long cached = parseKB("Cached"); + memAvailable = memFree + buffers + cached; + } + + totalGB = static_cast((memTotal + 1024 * 1024 - 1) / (1024 * 1024)); + freeGB = static_cast(memAvailable / (1024 * 1024)); +} + +void MemoryInfo::fetchModulesInfo() { + modules.clear(); + + std::string output = Platform::exec("dmidecode -t memory 2>/dev/null | grep -E 'Size:|Speed:|Type:' | head -20"); + + if (output.empty()) { + MemoryModule mod; + mod.capacity = std::to_string(totalGB) + "GB"; + mod.type = "Unknown"; + mod.speed = "Unknown"; + modules.push_back(mod); + return; + } + + std::istringstream iss(output); + std::string line; + MemoryModule current; + + while (std::getline(iss, line)) { + line = Platform::trim(line); + + if (line.find("Size:") == 0) { + if (!current.capacity.empty() && current.capacity.find("No Module") == std::string::npos) { + modules.push_back(current); + } + current = MemoryModule(); + + std::string size = line.substr(5); + size = Platform::trim(size); + if (size.find("No Module") == std::string::npos) { + current.capacity = size; + } + } + else if (line.find("Type:") == 0 && current.type.empty()) { + std::string type = Platform::trim(line.substr(5)); + if (type != "Unknown" && type != "Other") { + current.type = type; + } + } + else if (line.find("Speed:") == 0 && current.speed.empty()) { + std::string speed = Platform::trim(line.substr(6)); + if (speed != "Unknown") { + current.speed = speed; + } + } + } + + if (!current.capacity.empty() && current.capacity.find("No Module") == std::string::npos) { + modules.push_back(current); + } + + if (modules.empty()) { + MemoryModule mod; + mod.capacity = std::to_string(totalGB) + "GB"; + mod.type = "Unknown"; + mod.speed = "Unknown"; + modules.push_back(mod); + } +} + +int MemoryInfo::getTotal() const { return totalGB; } +int MemoryInfo::getFree() const { return freeGB; } + +int MemoryInfo::getUsedPercentage() const { + if (totalGB == 0) return 0; + double percentage = (static_cast(totalGB - freeGB) / totalGB) * 100; + if (percentage > 100.0) percentage = 100.0; + if (percentage < 0.0) percentage = 0.0; + return static_cast(percentage); +} + +const std::vector& MemoryInfo::getModules() const { return modules; } diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/NetworkInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/NetworkInfoLinux.cpp new file mode 100644 index 0000000..0886f64 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/NetworkInfoLinux.cpp @@ -0,0 +1,148 @@ +#include "../Platform.h" +#include "../HttpClient.h" +#include "../../NetworkInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::string getPrimaryInterface() { + struct ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) return ""; + + std::string primary; + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + + std::string name = ifa->ifa_name; + if (name == "lo") continue; + if (name.find("docker") == 0) continue; + if (name.find("virbr") == 0) continue; + if (name.find("br-") == 0) continue; + if (name.find("veth") == 0) continue; + + if (ifa->ifa_flags & IFF_UP) { + primary = name; + break; + } + } + + freeifaddrs(ifaddr); + return primary; +} + +std::string NetworkInfo::get_local_ip() { + struct ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) return "Unknown"; + + std::string result = "Unknown"; + + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + + std::string name = ifa->ifa_name; + if (name == "lo") continue; + if (name.find("docker") == 0) continue; + if (name.find("virbr") == 0) continue; + + if (!(ifa->ifa_flags & IFF_UP)) continue; + + char ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr, ip, sizeof(ip)); + + int prefix = 24; + if (ifa->ifa_netmask) { + uint32_t mask = ntohl(((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr); + prefix = 0; + while (mask) { + prefix += (mask & 1); + mask >>= 1; + } + } + + std::ostringstream oss; + oss << ip << "/" << prefix; + result = oss.str(); + break; + } + + freeifaddrs(ifaddr); + return result; +} + +std::string NetworkInfo::get_mac_address() { + std::string iface = getPrimaryInterface(); + if (iface.empty()) return "Unknown"; + + std::string path = "/sys/class/net/" + iface + "/address"; + std::string mac = Platform::readFileLine(path); + + if (!mac.empty()) { + std::transform(mac.begin(), mac.end(), mac.begin(), ::toupper); + return Platform::trim(mac); + } + + return "Unknown"; +} + +std::string NetworkInfo::get_locale() { + std::string locale = Platform::getEnv("LANG"); + if (!locale.empty()) { + size_t dot = locale.find('.'); + if (dot != std::string::npos) { + locale = locale.substr(0, dot); + } + std::replace(locale.begin(), locale.end(), '_', '-'); + return locale; + } + + return "en-US"; +} + +std::string NetworkInfo::get_network_name() { + if (Platform::commandExists("iwgetid")) { + std::string ssid = Platform::exec("iwgetid -r 2>/dev/null"); + ssid = Platform::trim(ssid); + if (!ssid.empty()) return ssid; + } + + if (Platform::commandExists("nmcli")) { + std::string result = Platform::exec("nmcli -t -f NAME connection show --active 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) return result; + } + + std::string iface = getPrimaryInterface(); + return iface.empty() ? "Unknown" : iface; +} + +std::string NetworkInfo::get_public_ip() { + Platform::HttpClient::Response resp = Platform::HttpClient::get("api.ipify.org", "/", 80, 5000); + if (resp.success) { + return Platform::trim(resp.body); + } + + resp = Platform::HttpClient::get("ifconfig.me", "/ip", 80, 5000); + if (resp.success) { + return Platform::trim(resp.body); + } + + return "Unknown"; +} + +std::string NetworkInfo::get_network_download_speed() { + return Platform::HttpClient::downloadSpeed("speed.cloudflare.com", "/__down", 1000000, 5000); +} + +std::string NetworkInfo::get_network_upload_speed() { + return Platform::HttpClient::uploadSpeed("speed.cloudflare.com", "/__up", 500000, 5000); +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/OSInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/OSInfoLinux.cpp new file mode 100644 index 0000000..cf5457a --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/OSInfoLinux.cpp @@ -0,0 +1,103 @@ +#include "../Platform.h" +#include "../../OSInfo.h" +#include +#include +#include +#include +#include +#include +#include + +std::string OSInfo::GetOSVersion() { + std::string content = Platform::readFile("/etc/os-release"); + std::string version = Platform::parseValue(content, "VERSION_ID", '='); + if (version.empty()) { + version = Platform::parseValue(content, "VERSION", '='); + } + version.erase(std::remove(version.begin(), version.end(), '"'), version.end()); + return version.empty() ? "Unknown" : version; +} + +std::string OSInfo::GetOSArchitecture() { + struct utsname buf; + if (uname(&buf) == 0) { + std::string machine = buf.machine; + if (machine == "x86_64" || machine == "amd64") return "64-bit"; + if (machine == "i386" || machine == "i686") return "32-bit"; + if (machine == "aarch64") return "ARM64"; + if (machine == "armv7l") return "ARM32"; + return machine; + } + return "Unknown"; +} + +std::string OSInfo::GetOSName() { + std::string content = Platform::readFile("/etc/os-release"); + std::string name = Platform::parseValue(content, "PRETTY_NAME", '='); + name.erase(std::remove(name.begin(), name.end(), '"'), name.end()); + + if (name.empty()) { + name = Platform::parseValue(content, "NAME", '='); + name.erase(std::remove(name.begin(), name.end(), '"'), name.end()); + std::string version = GetOSVersion(); + if (!version.empty() && version != "Unknown") { + name += " " + version; + } + } + + return name.empty() ? "Linux" : name; +} + +std::string OSInfo::get_os_install_date() { + struct stat st; + if (stat("/", &st) == 0) { + char buf[64]; + struct tm* tm_info = localtime(&st.st_ctime); + strftime(buf, sizeof(buf), "%Y-%m-%d", tm_info); + return std::string(buf); + } + + std::string result = Platform::exec("ls -lact --full-time /etc 2>/dev/null | tail -1 | awk '{print $6}'"); + return Platform::trim(result).empty() ? "N/A" : Platform::trim(result); +} + +std::string OSInfo::get_os_serial_number() { + std::string serial = Platform::readFileLine("/sys/class/dmi/id/product_serial"); + if (!serial.empty() && serial != "To Be Filled By O.E.M.") { + return Platform::trim(serial); + } + + serial = Platform::readFileLine("/sys/class/dmi/id/board_serial"); + if (!serial.empty() && serial != "To Be Filled By O.E.M.") { + return Platform::trim(serial); + } + + return "N/A (requires root)"; +} + +std::string OSInfo::get_os_uptime() { + std::string uptimeStr = Platform::readFileLine("/proc/uptime"); + if (uptimeStr.empty()) return "Unknown"; + + double uptime_seconds = std::stod(uptimeStr); + + int days = static_cast(uptime_seconds / 86400); + int hours = static_cast((uptime_seconds - days * 86400) / 3600); + int minutes = static_cast((uptime_seconds - days * 86400 - hours * 3600) / 60); + int seconds = static_cast(uptime_seconds) % 60; + + std::ostringstream ss; + ss << days << ":" + << std::setw(2) << std::setfill('0') << hours << ":" + << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds; + return ss.str(); +} + +std::string OSInfo::get_os_kernel_info() { + struct utsname buf; + if (uname(&buf) == 0) { + return std::string(buf.sysname) + " " + buf.release; + } + return "Linux"; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/PerformanceInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/PerformanceInfoLinux.cpp new file mode 100644 index 0000000..36006f9 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/PerformanceInfoLinux.cpp @@ -0,0 +1,127 @@ +#include "../Platform.h" +#include "../../PerformanceInfo.h" +#include +#include +#include +#include +#include +#include + +struct PerformanceInfo::Impl { + long prev_idle = 0; + long prev_total = 0; + bool first_call = true; +}; + +PerformanceInfo::PerformanceInfo() { + pImpl = new Impl(); +} + +PerformanceInfo::~PerformanceInfo() { + delete pImpl; + pImpl = nullptr; +} + +std::string PerformanceInfo::format_uptime(unsigned long long totalMilliseconds) { + unsigned long long totalSeconds = totalMilliseconds / 1000ULL; + int hours = static_cast(totalSeconds / 3600ULL); + int minutes = static_cast((totalSeconds % 3600ULL) / 60ULL); + int seconds = static_cast(totalSeconds % 60ULL); + return std::to_string(hours) + "h " + std::to_string(minutes) + "m " + std::to_string(seconds) + "s"; +} + +std::string PerformanceInfo::get_system_uptime() { + std::string uptimeStr = Platform::readFileLine("/proc/uptime"); + if (uptimeStr.empty()) return "Unknown"; + + double uptime_seconds = std::stod(uptimeStr); + unsigned long long ms = static_cast(uptime_seconds * 1000); + return format_uptime(ms); +} + +float PerformanceInfo::get_cpu_usage_percent() { + std::string stat = Platform::readFileLine("/proc/stat"); + if (stat.empty()) return 0.0f; + + long user, nice, system, idle, iowait, irq, softirq, steal; + sscanf(stat.c_str(), "cpu %ld %ld %ld %ld %ld %ld %ld %ld", + &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal); + + long idle_time = idle + iowait; + long total_time = user + nice + system + idle + iowait + irq + softirq + steal; + + if (pImpl->first_call) { + pImpl->prev_idle = idle_time; + pImpl->prev_total = total_time; + pImpl->first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return get_cpu_usage_percent(); + } + + long idle_delta = idle_time - pImpl->prev_idle; + long total_delta = total_time - pImpl->prev_total; + + pImpl->prev_idle = idle_time; + pImpl->prev_total = total_time; + + if (total_delta == 0) return 0.0f; + + return (1.0f - (float)idle_delta / (float)total_delta) * 100.0f; +} + +float PerformanceInfo::get_ram_usage_percent() { + std::string content = Platform::readFile("/proc/meminfo"); + + auto parseKB = [&](const std::string& key) -> long long { + std::string val = Platform::parseValue(content, key); + if (val.empty()) return 0; + return std::stoll(val); + }; + + long long memTotal = parseKB("MemTotal"); + long long memAvailable = parseKB("MemAvailable"); + + if (memAvailable == 0) { + long long memFree = parseKB("MemFree"); + long long buffers = parseKB("Buffers"); + long long cached = parseKB("Cached"); + memAvailable = memFree + buffers + cached; + } + + if (memTotal == 0) return 0.0f; + + long long used = memTotal - memAvailable; + return static_cast((used * 100.0) / memTotal); +} + +float PerformanceInfo::get_disk_usage_percent() { + struct statvfs stat; + if (statvfs("/", &stat) != 0) return 0.0f; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + unsigned long long free = stat.f_bfree * stat.f_frsize; + + if (total == 0) return 0.0f; + + unsigned long long used = total - free; + return static_cast((used * 100.0) / total); +} + +float PerformanceInfo::get_gpu_usage_percent() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null"); + if (!result.empty()) { + try { return std::stof(Platform::trim(result)); } + catch (...) {} + } + } + + std::string busyPath = "/sys/class/drm/card0/device/gpu_busy_percent"; + std::string busy = Platform::readFileLine(busyPath); + if (!busy.empty()) { + try { return std::stof(busy); } + catch (...) {} + } + + return 0.0f; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/StorageInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/StorageInfoLinux.cpp new file mode 100644 index 0000000..653e649 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/StorageInfoLinux.cpp @@ -0,0 +1,195 @@ +#include "../Platform.h" +#include "../../StorageInfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::set pseudoFS = {"proc", "sysfs", "devtmpfs", "tmpfs", "securityfs", + "cgroup", "cgroup2", "pstore", "debugfs", "hugetlbfs", "mqueue", "fusectl", + "configfs", "devpts", "ramfs", "binfmt_misc", "autofs", "tracefs", "overlay", "squashfs"}; + +static std::string getBlockDevice(const std::string& mountPath) { + FILE* mtab = setmntent("/proc/mounts", "r"); + if (!mtab) return ""; + + struct mntent* ent; + while ((ent = getmntent(mtab)) != nullptr) { + if (std::string(ent->mnt_dir) == mountPath) { + std::string dev = ent->mnt_fsname; + endmntent(mtab); + + if (dev.find("/dev/") == 0) { + std::string base = dev.substr(5); + while (!base.empty() && (std::isdigit(base.back()) || base.back() == 'p')) { + base.pop_back(); + } + return base; + } + return ""; + } + } + endmntent(mtab); + return ""; +} + +std::string StorageInfo::get_storage_type(const std::string&, const std::string& root_path, bool) { + std::string blockDev = getBlockDevice(root_path); + if (blockDev.empty()) return "Unknown"; + + std::string rotPath = "/sys/block/" + blockDev + "/queue/rotational"; + std::string rot = Platform::readFileLine(rotPath); + + if (!rot.empty()) { + if (Platform::trim(rot) == "0") return "SSD"; + if (Platform::trim(rot) == "1") return "HDD"; + } + + if (blockDev.find("nvme") == 0) return "SSD"; + if (blockDev.find("mmcblk") == 0) return "SSD"; + + std::string removePath = "/sys/block/" + blockDev + "/removable"; + std::string removable = Platform::readFileLine(removePath); + if (!removable.empty() && Platform::trim(removable) == "1") return "USB"; + + return "Unknown"; +} + +static double measureSpeed(const std::string& path, bool write) { + const size_t BUF_SIZE = 16 * 1024 * 1024; + std::vector buffer(BUF_SIZE, 'X'); + + std::string testFile = path + "/.binaryfetch_speed_test"; + + if (write) { + auto start = std::chrono::high_resolution_clock::now(); + int fd = open(testFile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644); + if (fd < 0) return 0.0; + + ssize_t written = ::write(fd, buffer.data(), BUF_SIZE); + fsync(fd); + close(fd); + + auto end = std::chrono::high_resolution_clock::now(); + + if (written <= 0) { + unlink(testFile.c_str()); + return 0.0; + } + + double seconds = std::chrono::duration(end - start).count(); + if (seconds < 0.001) seconds = 0.001; + + return (written / (1024.0 * 1024.0)) / seconds; + } else { + int fd = open(testFile.c_str(), O_RDONLY); + if (fd < 0) return 0.0; + + auto start = std::chrono::high_resolution_clock::now(); + ssize_t bytesRead = read(fd, buffer.data(), BUF_SIZE); + close(fd); + unlink(testFile.c_str()); + + auto end = std::chrono::high_resolution_clock::now(); + + if (bytesRead <= 0) return 0.0; + + double seconds = std::chrono::duration(end - start).count(); + if (seconds < 0.001) seconds = 0.001; + + return (bytesRead / (1024.0 * 1024.0)) / seconds; + } +} + +std::vector StorageInfo::get_all_storage_info() { + std::vector all_disks; + + FILE* mtab = setmntent("/proc/mounts", "r"); + if (!mtab) return all_disks; + + std::set seen; + struct mntent* ent; + + while ((ent = getmntent(mtab)) != nullptr) { + std::string fstype = ent->mnt_type; + std::string device = ent->mnt_fsname; + std::string mountpoint = ent->mnt_dir; + + if (pseudoFS.count(fstype) || device.find("/dev/") != 0) continue; + if (seen.count(device)) continue; + seen.insert(device); + + struct statvfs stat; + if (statvfs(mountpoint.c_str(), &stat) != 0) continue; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + unsigned long long free = stat.f_bfree * stat.f_frsize; + unsigned long long used = total - free; + + if (total < 100 * 1024 * 1024) continue; + + double totalGiB = total / (1024.0 * 1024.0 * 1024.0); + double usedGiB = used / (1024.0 * 1024.0 * 1024.0); + double usedPercent = (total > 0) ? (used * 100.0 / total) : 0.0; + + storage_data disk; + disk.drive_letter = "Disk (" + mountpoint + ")"; + + std::ostringstream usedStr, totalStr, percentStr; + usedStr << std::fixed << std::setprecision(2) << usedGiB; + totalStr << std::fixed << std::setprecision(2) << totalGiB; + percentStr << "(" << static_cast(usedPercent) << "%)"; + + disk.used_space = usedStr.str(); + disk.total_space = totalStr.str(); + disk.used_percentage = percentStr.str(); + disk.file_system = fstype; + disk.storage_type = get_storage_type("", mountpoint, false); + disk.is_external = (disk.storage_type == "USB"); + + double w = measureSpeed(mountpoint, true); + usleep(100000); + double r = measureSpeed(mountpoint, false); + + std::ostringstream readStr, writeStr; + readStr << std::fixed << std::setprecision(2) << r; + writeStr << std::fixed << std::setprecision(2) << w; + disk.read_speed = readStr.str(); + disk.write_speed = writeStr.str(); + + if (disk.storage_type == "USB") { + disk.predicted_read_speed = "100"; + disk.predicted_write_speed = "80"; + } else if (disk.storage_type == "SSD") { + disk.predicted_read_speed = "500"; + disk.predicted_write_speed = "450"; + } else if (disk.storage_type == "HDD") { + disk.predicted_read_speed = "140"; + disk.predicted_write_speed = "120"; + } else { + disk.predicted_read_speed = "---"; + disk.predicted_write_speed = "---"; + } + + disk.serial_number = "N/A"; + + all_disks.push_back(disk); + } + + endmntent(mtab); + return all_disks; +} + +void StorageInfo::process_storage_info(std::function callback) { + auto disks = get_all_storage_info(); + for (const auto& disk : disks) { + callback(disk); + } +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/linux/SystemInfoLinux.cpp b/project_binary_fetch/binary_fetch_v1/platform/linux/SystemInfoLinux.cpp new file mode 100644 index 0000000..3089809 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/linux/SystemInfoLinux.cpp @@ -0,0 +1,41 @@ +#include "../Platform.h" +#include "../../SystemInfo.h" +#include + +static std::string readDMI(const std::string& file) { + std::string path = "/sys/class/dmi/id/" + file; + std::string value = Platform::readFileLine(path); + value = Platform::trim(value); + + if (value.empty() || value == "To Be Filled By O.E.M." || value == "Default string" || value == "Not Specified") { + return "N/A"; + } + return value; +} + +SystemInfo::SystemInfo() {} +SystemInfo::~SystemInfo() {} + +std::string SystemInfo::get_bios_vendor() { + return readDMI("bios_vendor"); +} + +std::string SystemInfo::get_bios_version() { + return readDMI("bios_version"); +} + +std::string SystemInfo::get_bios_date() { + return readDMI("bios_date"); +} + +std::string SystemInfo::get_motherboard_manufacturer() { + return readDMI("board_vendor"); +} + +std::string SystemInfo::get_motherboard_model() { + return readDMI("board_name"); +} + +std::string SystemInfo::read_registry_value(const std::string& /*subkey*/, const std::string& /*valueName*/) { + return ""; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactAudioPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactAudioPosix.cpp new file mode 100644 index 0000000..ec8b780 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactAudioPosix.cpp @@ -0,0 +1,243 @@ +#include "../../CompactAudio.h" +#include "../Platform.h" +#include +#include +#include + +#if PLATFORM_LINUX + +static std::string cachedOutputDevice; +static std::string cachedInputDevice; +static bool devicesCached = false; + +static std::string shortenDeviceName(const std::string& name) { + std::string result = name; + + std::vector removePatterns = { + "Family 17h (Models 00h-0fh) ", + "Family 17h ", + "(Models 00h-0fh) ", + "High Definition Audio Controller ", + "HD Audio Controller ", + "Audio Controller ", + "Controller ", + "(High Definition Audio Device)", + "(High Definition Audio)", + "High Definition Audio Device", + }; + + for (const auto& pattern : removePatterns) { + size_t pos; + while ((pos = result.find(pattern)) != std::string::npos) { + result.erase(pos, pattern.length()); + } + } + + while (!result.empty() && result[0] == ' ') result.erase(0, 1); + while (!result.empty() && result.back() == ' ') result.pop_back(); + + while (result.find(" ") != std::string::npos) { + size_t pos = result.find(" "); + result.erase(pos, 1); + } + + if (result.empty()) result = name; + + return result; +} + +static void cacheAudioDevices() { + if (devicesCached) return; + + if (Platform::commandExists("wpctl")) { + std::string status = Platform::exec("wpctl status 2>/dev/null"); + if (!status.empty()) { + std::istringstream iss(status); + std::string line; + bool inSinks = false; + bool inSources = false; + + while (std::getline(iss, line)) { + if (line.find("Sinks:") != std::string::npos) { + inSinks = true; + inSources = false; + continue; + } + if (line.find("Sources:") != std::string::npos) { + inSinks = false; + inSources = true; + continue; + } + if (line.find("Filters:") != std::string::npos || + line.find("Streams:") != std::string::npos) { + inSinks = false; + inSources = false; + continue; + } + + if ((inSinks || inSources) && line.find("*") != std::string::npos) { + size_t dotPos = line.find('.'); + size_t bracketPos = line.find('['); + if (dotPos != std::string::npos) { + size_t nameEnd = (bracketPos != std::string::npos) ? bracketPos : line.length(); + std::string name = Platform::trim(line.substr(dotPos + 1, nameEnd - dotPos - 1)); + + if (inSinks && cachedOutputDevice.empty()) { + cachedOutputDevice = name; + } else if (inSources && cachedInputDevice.empty()) { + cachedInputDevice = name; + } + } + } + } + } + } + + if ((cachedOutputDevice.empty() || cachedInputDevice.empty()) && Platform::commandExists("pactl")) { + if (cachedOutputDevice.empty()) { + std::string sinkName = Platform::trim(Platform::exec("pactl get-default-sink 2>/dev/null")); + if (!sinkName.empty()) { + std::string info = Platform::exec("pactl list sinks 2>/dev/null | grep -A5 'Name: " + sinkName + "' | grep 'Description:' | head -1"); + if (!info.empty()) { + size_t pos = info.find(':'); + if (pos != std::string::npos) { + cachedOutputDevice = Platform::trim(info.substr(pos + 1)); + } + } + if (cachedOutputDevice.empty()) { + cachedOutputDevice = sinkName; + } + } + } + + if (cachedInputDevice.empty()) { + std::string sourceName = Platform::trim(Platform::exec("pactl get-default-source 2>/dev/null")); + if (!sourceName.empty() && sourceName.find("monitor") == std::string::npos) { + std::string info = Platform::exec("pactl list sources 2>/dev/null | grep -A5 'Name: " + sourceName + "' | grep 'Description:' | head -1"); + if (!info.empty()) { + size_t pos = info.find(':'); + if (pos != std::string::npos) { + cachedInputDevice = Platform::trim(info.substr(pos + 1)); + } + } + if (cachedInputDevice.empty()) { + cachedInputDevice = sourceName; + } + } + } + } + + if ((cachedOutputDevice.empty() || cachedInputDevice.empty()) && Platform::commandExists("aplay")) { + if (cachedOutputDevice.empty()) { + std::string output = Platform::exec("aplay -l 2>/dev/null | grep 'card' | head -1"); + if (!output.empty()) { + size_t bracket = output.find('['); + size_t bracket2 = output.find(']'); + if (bracket != std::string::npos && bracket2 != std::string::npos && bracket2 > bracket) { + cachedOutputDevice = output.substr(bracket + 1, bracket2 - bracket - 1); + } + } + } + + if (cachedInputDevice.empty()) { + std::string output = Platform::exec("arecord -l 2>/dev/null | grep 'card' | head -1"); + if (!output.empty()) { + size_t bracket = output.find('['); + size_t bracket2 = output.find(']'); + if (bracket != std::string::npos && bracket2 != std::string::npos && bracket2 > bracket) { + cachedInputDevice = output.substr(bracket + 1, bracket2 - bracket - 1); + } + } + } + } + + if (cachedOutputDevice.empty()) cachedOutputDevice = "Default Audio Output"; + if (cachedInputDevice.empty()) cachedInputDevice = "Default Audio Input"; + + devicesCached = true; +} + +std::string CompactAudio::active_audio_output() { + cacheAudioDevices(); + return shortenDeviceName(cachedOutputDevice); +} + +std::string CompactAudio::active_audio_output_status() { + cacheAudioDevices(); + return cachedOutputDevice.empty() || cachedOutputDevice == "Default Audio Output" ? "(Unknown)" : "(Active)"; +} + +std::string CompactAudio::active_audio_input() { + cacheAudioDevices(); + return shortenDeviceName(cachedInputDevice); +} + +std::string CompactAudio::active_audio_input_status() { + cacheAudioDevices(); + return cachedInputDevice.empty() || cachedInputDevice == "Default Audio Input" ? "(Unknown)" : "(Active)"; +} + +#elif PLATFORM_FREEBSD + +std::string CompactAudio::active_audio_output() { + std::string sndstat = Platform::readFile("/dev/sndstat"); + if (!sndstat.empty()) { + std::istringstream iss(sndstat); + std::string line; + while (std::getline(iss, line)) { + if (line.find("default") != std::string::npos || + line.find("pcm0") != std::string::npos) { + size_t angleStart = line.find('<'); + size_t angleEnd = line.find('>'); + if (angleStart != std::string::npos && angleEnd != std::string::npos) { + return line.substr(angleStart + 1, angleEnd - angleStart - 1); + } + } + } + } + + if (Platform::commandExists("mixer")) { + std::string result = Platform::exec("mixer -S 2>/dev/null | head -1"); + if (!result.empty()) { + return Platform::trim(result); + } + } + + return "Default Audio Output"; +} + +std::string CompactAudio::active_audio_output_status() { + if (Platform::fileExists("/dev/dsp") || Platform::fileExists("/dev/dsp0")) { + return "(Active)"; + } + return "(Unknown)"; +} + +std::string CompactAudio::active_audio_input() { + std::string sndstat = Platform::readFile("/dev/sndstat"); + if (!sndstat.empty()) { + std::istringstream iss(sndstat); + std::string line; + while (std::getline(iss, line)) { + if (line.find("rec") != std::string::npos || + line.find("input") != std::string::npos) { + size_t angleStart = line.find('<'); + size_t angleEnd = line.find('>'); + if (angleStart != std::string::npos && angleEnd != std::string::npos) { + return line.substr(angleStart + 1, angleEnd - angleStart - 1); + } + } + } + } + + return "Default Audio Input"; +} + +std::string CompactAudio::active_audio_input_status() { + if (Platform::fileExists("/dev/dsp") || Platform::fileExists("/dev/dsp0")) { + return "(Active)"; + } + return "(Unknown)"; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactCPUPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactCPUPosix.cpp new file mode 100644 index 0000000..6571305 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactCPUPosix.cpp @@ -0,0 +1,151 @@ +#include "CompactCPU.h" +#include "platform/Platform.h" +#include +#include +#include +#include + +#if PLATFORM_LINUX + +static long prev_idle = 0; +static long prev_total = 0; +static bool first_call = true; + +std::string CompactCPU::getCPUName() { + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string model = Platform::parseValue(content, "model name"); + return model.empty() ? "Unknown CPU" : model; +} + +std::string CompactCPU::getCPUCores() { + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string cores = Platform::parseValue(content, "cpu cores"); + if (!cores.empty()) { + return cores; + } + int count = 0; + std::istringstream iss(content); + std::string line; + while (std::getline(iss, line)) { + if (line.find("processor") == 0) count++; + } + return std::to_string(count > 0 ? count / 2 : 1); +} + +std::string CompactCPU::getCPUThreads() { + std::string content = Platform::readFile("/proc/cpuinfo"); + int count = 0; + std::istringstream iss(content); + std::string line; + while (std::getline(iss, line)) { + if (line.find("processor") == 0) count++; + } + return std::to_string(count > 0 ? count : 1); +} + +double CompactCPU::getClockSpeed() { + std::string freq = Platform::readFileLine("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"); + if (!freq.empty()) { + return std::stof(freq) / 1000000.0; + } + + std::string content = Platform::readFile("/proc/cpuinfo"); + std::string mhz = Platform::parseValue(content, "cpu MHz"); + if (!mhz.empty()) { + return std::stof(mhz) / 1000.0; + } + return 0.0; +} + +double CompactCPU::getUsagePercent() { + std::string stat = Platform::readFileLine("/proc/stat"); + if (stat.empty()) return 0.0; + + long user, nice, system, idle, iowait, irq, softirq, steal; + sscanf(stat.c_str(), "cpu %ld %ld %ld %ld %ld %ld %ld %ld", + &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal); + + long idle_time = idle + iowait; + long total_time = user + nice + system + idle + iowait + irq + softirq + steal; + + if (first_call) { + prev_idle = idle_time; + prev_total = total_time; + first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return getUsagePercent(); + } + + long idle_delta = idle_time - prev_idle; + long total_delta = total_time - prev_total; + + prev_idle = idle_time; + prev_total = total_time; + + if (total_delta == 0) return 0.0; + + return (1.0 - (double)idle_delta / (double)total_delta) * 100.0; +} + +#elif PLATFORM_FREEBSD + +static long prev_cp_time[5] = {0}; +static bool first_call = true; + +std::string CompactCPU::getCPUName() { + std::string model = Platform::sysctlString("hw.model"); + return model.empty() ? "Unknown CPU" : model; +} + +std::string CompactCPU::getCPUCores() { + int ncpu = static_cast(Platform::sysctlLong("hw.ncpu")); + return std::to_string(ncpu > 0 ? ncpu : 1); +} + +std::string CompactCPU::getCPUThreads() { + int ncpu = static_cast(Platform::sysctlLong("hw.ncpu")); + return std::to_string(ncpu > 0 ? ncpu : 1); +} + +double CompactCPU::getClockSpeed() { + std::string freq = Platform::exec("sysctl -n dev.cpu.0.freq 2>/dev/null"); + freq = Platform::trim(freq); + if (!freq.empty()) { + try { + return std::stof(freq) / 1000.0; + } catch (...) {} + } + return 0.0; +} + +double CompactCPU::getUsagePercent() { + long cp_time[5]; + size_t len = sizeof(cp_time); + + if (sysctlbyname("kern.cp_time", cp_time, &len, nullptr, 0) != 0) { + return 0.0; + } + + if (first_call) { + memcpy(prev_cp_time, cp_time, sizeof(cp_time)); + first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return getUsagePercent(); + } + + long total_delta = 0; + long idle_delta = 0; + + for (int i = 0; i < 5; i++) { + total_delta += cp_time[i] - prev_cp_time[i]; + } + idle_delta = cp_time[4] - prev_cp_time[4]; + + memcpy(prev_cp_time, cp_time, sizeof(cp_time)); + + if (total_delta == 0) return 0.0; + + return (1.0 - (double)idle_delta / (double)total_delta) * 100.0; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactGPUPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactGPUPosix.cpp new file mode 100644 index 0000000..f121855 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactGPUPosix.cpp @@ -0,0 +1,280 @@ +#include "../../CompactGPU.h" +#include "../Platform.h" +#include +#include +#include +#include +#include + +#if PLATFORM_POSIX + +static std::map pciIdToName = { + // AMD Radeon RX 500 series + {"1002:67df", "AMD Radeon RX 580"}, + {"1002:67ef", "AMD Radeon RX 560"}, + {"1002:67ff", "AMD Radeon RX 560X"}, + {"1002:699f", "AMD Radeon RX 550"}, + // AMD Radeon RX 5000 series (RDNA) + {"1002:731f", "AMD Radeon RX 5700 XT"}, + {"1002:7340", "AMD Radeon RX 5700"}, + {"1002:7341", "AMD Radeon RX 5600 XT"}, + // AMD Radeon RX 6000 series (RDNA 2) + {"1002:73bf", "AMD Radeon RX 6900 XT"}, + {"1002:73af", "AMD Radeon RX 6800 XT"}, + {"1002:73a5", "AMD Radeon RX 6800"}, + {"1002:73df", "AMD Radeon RX 6700 XT"}, + {"1002:73ff", "AMD Radeon RX 6600 XT"}, + {"1002:73e3", "AMD Radeon RX 6600"}, + // AMD Radeon RX 7000 series (RDNA 3) + {"1002:744c", "AMD Radeon RX 7900 XTX"}, + {"1002:7448", "AMD Radeon RX 7900 XT"}, + {"1002:7480", "AMD Radeon RX 7600"}, + {"1002:7483", "AMD Radeon RX 7600 XT"}, + // NVIDIA RTX 40 series + {"10de:2684", "NVIDIA GeForce RTX 4090"}, + {"10de:2702", "NVIDIA GeForce RTX 4080 SUPER"}, + {"10de:2704", "NVIDIA GeForce RTX 4080"}, + {"10de:2782", "NVIDIA GeForce RTX 4070 Ti SUPER"}, + {"10de:2783", "NVIDIA GeForce RTX 4070 Ti"}, + {"10de:2786", "NVIDIA GeForce RTX 4070 SUPER"}, + {"10de:2788", "NVIDIA GeForce RTX 4070"}, + {"10de:27a0", "NVIDIA GeForce RTX 4060 Ti"}, + {"10de:27b0", "NVIDIA GeForce RTX 4060"}, + // NVIDIA RTX 30 series + {"10de:2204", "NVIDIA GeForce RTX 3090"}, + {"10de:2203", "NVIDIA GeForce RTX 3090 Ti"}, + {"10de:2206", "NVIDIA GeForce RTX 3080"}, + {"10de:2208", "NVIDIA GeForce RTX 3080 Ti"}, + {"10de:2216", "NVIDIA GeForce RTX 3070"}, + {"10de:2414", "NVIDIA GeForce RTX 3070 Ti"}, + {"10de:2484", "NVIDIA GeForce RTX 3060"}, + {"10de:2486", "NVIDIA GeForce RTX 3060 Ti"}, + // NVIDIA RTX 20 series + {"10de:1e04", "NVIDIA GeForce RTX 2080 Ti"}, + {"10de:1e07", "NVIDIA GeForce RTX 2080 SUPER"}, + {"10de:1e82", "NVIDIA GeForce RTX 2080"}, + {"10de:1f07", "NVIDIA GeForce RTX 2070 SUPER"}, + {"10de:1f02", "NVIDIA GeForce RTX 2070"}, + {"10de:1f08", "NVIDIA GeForce RTX 2060 SUPER"}, + {"10de:1f47", "NVIDIA GeForce RTX 2060"}, + // NVIDIA GTX 16 series + {"10de:2182", "NVIDIA GeForce GTX 1660 Ti"}, + {"10de:2184", "NVIDIA GeForce GTX 1660 SUPER"}, + {"10de:2187", "NVIDIA GeForce GTX 1650 SUPER"}, + {"10de:1f82", "NVIDIA GeForce GTX 1650"}, + // Intel Arc + {"8086:56a0", "Intel Arc A770"}, + {"8086:56a1", "Intel Arc A750"}, + {"8086:56a5", "Intel Arc A580"}, + {"8086:5690", "Intel Arc A380"}, + // Intel Integrated + {"8086:9a49", "Intel Iris Xe Graphics"}, + {"8086:a7a0", "Intel Raptor Lake-P GT2"}, + {"8086:46a6", "Intel Alder Lake-P GT2"}, +}; + +static std::string getVendorName(const std::string& vendorId) { + if (vendorId == "1002" || vendorId == "0x1002") return "AMD"; + if (vendorId == "10de" || vendorId == "0x10de") return "NVIDIA"; + if (vendorId == "8086" || vendorId == "0x8086") return "Intel"; + return "Unknown"; +} + +static std::string getGPUNameFromSysfs() { + DIR* dir = opendir("/sys/class/drm"); + if (!dir) return ""; + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") == std::string::npos) { + std::string devicePath = "/sys/class/drm/" + name + "/device/"; + std::string uevent = Platform::readFile(devicePath + "uevent"); + + std::string pciId; + std::istringstream iss(uevent); + std::string line; + while (std::getline(iss, line)) { + if (line.find("PCI_ID=") == 0) { + pciId = line.substr(7); + for (char& c : pciId) c = std::tolower(c); + break; + } + } + + if (!pciId.empty()) { + closedir(dir); + + auto it = pciIdToName.find(pciId); + if (it != pciIdToName.end()) { + return it->second; + } + + std::string vendor = getVendorName(pciId.substr(0, 4)); + return vendor + " GPU (" + pciId + ")"; + } + } + } + closedir(dir); + return ""; +} + +static std::string getGPUNameFromLspci() { + std::string output = Platform::exec("lspci 2>/dev/null"); + if (output.empty()) return ""; + + std::istringstream iss(output); + std::string line; + + while (std::getline(iss, line)) { + bool isVGA = line.find("0300") != std::string::npos || + line.find("0302") != std::string::npos || + line.find("0380") != std::string::npos || + line.find("VGA") != std::string::npos || + line.find("3D") != std::string::npos || + line.find("Display") != std::string::npos; + + if (isVGA) { + size_t colonPos = line.rfind(':'); + if (colonPos != std::string::npos && colonPos > 5) { + std::string pciId = Platform::trim(line.substr(colonPos - 9, 9)); + for (char& c : pciId) c = std::tolower(c); + + auto it = pciIdToName.find(pciId); + if (it != pciIdToName.end()) { + return it->second; + } + + std::string vendor = getVendorName(pciId.substr(0, 4)); + return vendor + " GPU"; + } + } + } + + return ""; +} + +std::string CompactGPU::getGPUName() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) return result; + } + + std::string name = getGPUNameFromSysfs(); + if (!name.empty()) return name; + + name = getGPUNameFromLspci(); + if (!name.empty()) return name; + + return "Unknown GPU"; +} + +int CompactGPU::getGPUUsagePercent() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) { + try { return std::stoi(result); } + catch (...) {} + } + } + + DIR* dir = opendir("/sys/class/drm"); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") == std::string::npos) { + std::string busyPath = "/sys/class/drm/" + name + "/device/gpu_busy_percent"; + std::string busy = Platform::readFileLine(busyPath); + if (!busy.empty()) { + closedir(dir); + try { return std::stoi(Platform::trim(busy)); } + catch (...) {} + } + } + } + closedir(dir); + } + + return 0; +} + +double CompactGPU::getVRAMGB() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) { + try { + float memMB = std::stof(result); + return memMB / 1024.0; + } catch (...) {} + } + } + + DIR* dir = opendir("/sys/class/drm"); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") == std::string::npos) { + std::string memPath = "/sys/class/drm/" + name + "/device/mem_info_vram_total"; + std::string memInfo = Platform::readFileLine(memPath); + if (!memInfo.empty()) { + closedir(dir); + try { + unsigned long long bytes = std::stoull(memInfo); + return bytes / (1024.0 * 1024.0 * 1024.0); + } catch (...) {} + } + } + } + closedir(dir); + } + + return 0.0; +} + +std::string CompactGPU::getGPUFrequency() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=clocks.gr --format=csv,noheader,nounits 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) { + return result + " MHz"; + } + } + + DIR* dir = opendir("/sys/class/drm"); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") == std::string::npos) { + std::string freqPath = "/sys/class/drm/" + name + "/device/pp_dpm_sclk"; + std::string freq = Platform::readFile(freqPath); + if (!freq.empty()) { + std::istringstream iss(freq); + std::string line; + while (std::getline(iss, line)) { + if (line.find('*') != std::string::npos) { + size_t colonPos = line.find(':'); + size_t mhzPos = line.find("Mhz"); + if (mhzPos == std::string::npos) mhzPos = line.find("MHz"); + if (colonPos != std::string::npos && mhzPos != std::string::npos) { + std::string clockStr = Platform::trim(line.substr(colonPos + 1, mhzPos - colonPos - 1)); + closedir(dir); + return clockStr + " MHz"; + } + } + } + } + } + } + closedir(dir); + } + + return "N/A"; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactMemoryPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactMemoryPosix.cpp new file mode 100644 index 0000000..d3f6dfd --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactMemoryPosix.cpp @@ -0,0 +1,131 @@ +#include "CompactMemory.h" +#include "platform/Platform.h" +#include + +#if PLATFORM_LINUX + +double CompactMemory::get_total_memory() { + std::string content = Platform::readFile("/proc/meminfo"); + std::string val = Platform::parseValue(content, "MemTotal"); + if (!val.empty()) { + long long kb = std::stoll(val); + return static_cast(kb) / (1024.0 * 1024.0); + } + return 0.0; +} + +double CompactMemory::get_free_memory() { + std::string content = Platform::readFile("/proc/meminfo"); + + std::string available = Platform::parseValue(content, "MemAvailable"); + if (!available.empty()) { + long long kb = std::stoll(available); + return static_cast(kb) / (1024.0 * 1024.0); + } + + long long memFree = 0, buffers = 0, cached = 0; + std::string val = Platform::parseValue(content, "MemFree"); + if (!val.empty()) memFree = std::stoll(val); + val = Platform::parseValue(content, "Buffers"); + if (!val.empty()) buffers = std::stoll(val); + val = Platform::parseValue(content, "Cached"); + if (!val.empty()) cached = std::stoll(val); + + long long freeKb = memFree + buffers + cached; + return static_cast(freeKb) / (1024.0 * 1024.0); +} + +double CompactMemory::get_used_memory_percent() { + std::string content = Platform::readFile("/proc/meminfo"); + + long long memTotal = 0, memAvailable = 0; + std::string val = Platform::parseValue(content, "MemTotal"); + if (!val.empty()) memTotal = std::stoll(val); + + val = Platform::parseValue(content, "MemAvailable"); + if (!val.empty()) { + memAvailable = std::stoll(val); + } else { + long long memFree = 0, buffers = 0, cached = 0; + val = Platform::parseValue(content, "MemFree"); + if (!val.empty()) memFree = std::stoll(val); + val = Platform::parseValue(content, "Buffers"); + if (!val.empty()) buffers = std::stoll(val); + val = Platform::parseValue(content, "Cached"); + if (!val.empty()) cached = std::stoll(val); + memAvailable = memFree + buffers + cached; + } + + if (memTotal == 0) return 0.0; + long long used = memTotal - memAvailable; + return static_cast(used * 100) / static_cast(memTotal); +} + +int CompactMemory::memory_slot_used() { + if (Platform::commandExists("dmidecode")) { + std::string result = Platform::exec("sudo dmidecode -t memory 2>/dev/null | grep -c 'Size:.*MB\\|Size:.*GB' || echo 0"); + result = Platform::trim(result); + if (!result.empty()) { + try { return std::stoi(result); } + catch (...) {} + } + } + return 0; +} + +int CompactMemory::memory_slot_available() { + if (Platform::commandExists("dmidecode")) { + std::string result = Platform::exec("sudo dmidecode -t memory 2>/dev/null | grep -c 'Size:' || echo 0"); + result = Platform::trim(result); + if (!result.empty()) { + try { return std::stoi(result); } + catch (...) {} + } + } + return 0; +} + +#elif PLATFORM_FREEBSD + +double CompactMemory::get_total_memory() { + unsigned long physmem = Platform::sysctlULong("hw.physmem"); + return static_cast(physmem) / (1024.0 * 1024.0 * 1024.0); +} + +double CompactMemory::get_free_memory() { + unsigned long pagesize = Platform::sysctlULong("hw.pagesize"); + if (pagesize == 0) pagesize = 4096; + + unsigned long free_count = Platform::sysctlULong("vm.stats.vm.v_free_count"); + unsigned long inactive = Platform::sysctlULong("vm.stats.vm.v_inactive_count"); + unsigned long cache = Platform::sysctlULong("vm.stats.vm.v_cache_count"); + + unsigned long available = (free_count + inactive + cache) * pagesize; + return static_cast(available) / (1024.0 * 1024.0 * 1024.0); +} + +double CompactMemory::get_used_memory_percent() { + unsigned long physmem = Platform::sysctlULong("hw.physmem"); + unsigned long pagesize = Platform::sysctlULong("hw.pagesize"); + if (pagesize == 0) pagesize = 4096; + + unsigned long free_count = Platform::sysctlULong("vm.stats.vm.v_free_count"); + unsigned long inactive = Platform::sysctlULong("vm.stats.vm.v_inactive_count"); + unsigned long cache = Platform::sysctlULong("vm.stats.vm.v_cache_count"); + + unsigned long available = (free_count + inactive + cache) * pagesize; + + if (physmem == 0) return 0.0; + unsigned long used = physmem - available; + return static_cast(used * 100) / static_cast(physmem); +} + +int CompactMemory::memory_slot_used() { + return 0; +} + +int CompactMemory::memory_slot_available() { + return 0; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactNetworkPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactNetworkPosix.cpp new file mode 100644 index 0000000..5df1c19 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactNetworkPosix.cpp @@ -0,0 +1,107 @@ +#include "CompactNetwork.h" +#include "platform/Platform.h" +#include +#include +#include +#include +#include + +#if PLATFORM_POSIX + +std::string CompactNetwork::get_wifi_ssid() { + if (Platform::commandExists("iwgetid")) { + std::string ssid = Platform::exec("iwgetid -r 2>/dev/null"); + ssid = Platform::trim(ssid); + if (!ssid.empty()) return ssid; + } + + if (Platform::commandExists("nmcli")) { + std::string result = Platform::exec("nmcli -t -f active,ssid dev wifi 2>/dev/null | grep '^yes'"); + if (!result.empty()) { + size_t colonPos = result.find(':'); + if (colonPos != std::string::npos) { + return Platform::trim(result.substr(colonPos + 1)); + } + } + } + + return ""; +} + +std::string CompactNetwork::get_ethernet_name() { + struct ifaddrs* ifaddr = nullptr; + if (getifaddrs(&ifaddr) == -1) return "Ethernet"; + + std::string result = "Ethernet"; + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + if (!(ifa->ifa_flags & IFF_UP)) continue; + if (ifa->ifa_flags & IFF_LOOPBACK) continue; + + std::string name = ifa->ifa_name; + if (name.find("eth") == 0 || name.find("en") == 0) { + result = name; + break; + } + } + + freeifaddrs(ifaddr); + return result; +} + +std::string CompactNetwork::get_network_name() { + std::string ssid = get_wifi_ssid(); + if (!ssid.empty()) return ssid; + return get_ethernet_name(); +} + +std::string CompactNetwork::get_network_type() { + struct ifaddrs* ifaddr = nullptr; + if (getifaddrs(&ifaddr) == -1) return "Unknown"; + + std::string type = "Unknown"; + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + if (!(ifa->ifa_flags & IFF_UP)) continue; + if (ifa->ifa_flags & IFF_LOOPBACK) continue; + + std::string name = ifa->ifa_name; + if (name.find("wl") == 0 || name.find("wlan") == 0) { + type = "WiFi"; + break; + } + if (name.find("eth") == 0 || name.find("en") == 0) { + type = "Ethernet"; + } + } + + freeifaddrs(ifaddr); + return type; +} + +std::string CompactNetwork::get_network_ip() { + struct ifaddrs* ifaddr = nullptr; + if (getifaddrs(&ifaddr) == -1) return "Unknown"; + + std::string ip = "Unknown"; + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; + if (ifa->ifa_addr->sa_family != AF_INET) continue; + if (!(ifa->ifa_flags & IFF_UP)) continue; + if (ifa->ifa_flags & IFF_LOOPBACK) continue; + + char addrBuf[INET_ADDRSTRLEN]; + struct sockaddr_in* addr = reinterpret_cast(ifa->ifa_addr); + if (inet_ntop(AF_INET, &addr->sin_addr, addrBuf, sizeof(addrBuf))) { + ip = addrBuf; + break; + } + } + + freeifaddrs(ifaddr); + return ip; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactOSPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactOSPosix.cpp new file mode 100644 index 0000000..01f1462 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactOSPosix.cpp @@ -0,0 +1,112 @@ +#include "CompactOS.h" +#include "platform/Platform.h" +#include +#include + +#if PLATFORM_LINUX + +std::string CompactOS::getOSName() { + std::string content = Platform::readFile("/etc/os-release"); + std::string name = Platform::parseValue(content, "PRETTY_NAME", '='); + name.erase(std::remove(name.begin(), name.end(), '"'), name.end()); + + if (name.empty()) { + name = Platform::parseValue(content, "NAME", '='); + name.erase(std::remove(name.begin(), name.end(), '"'), name.end()); + } + + return name.empty() ? "Linux" : name; +} + +std::string CompactOS::getOSBuild() { + struct utsname buf; + if (uname(&buf) == 0) { + return std::string(buf.release); + } + return "Unknown"; +} + +std::string CompactOS::getUptime() { + std::string uptimeStr = Platform::readFileLine("/proc/uptime"); + if (uptimeStr.empty()) return "Unknown"; + + double uptime_seconds = std::stod(uptimeStr); + int days = static_cast(uptime_seconds / 86400); + int hours = static_cast((uptime_seconds - days * 86400) / 3600); + int minutes = static_cast((uptime_seconds - days * 86400 - hours * 3600) / 60); + + std::ostringstream oss; + if (days > 0) oss << days << "d "; + if (hours > 0) oss << hours << "h "; + oss << minutes << "m"; + return oss.str(); +} + +std::string CompactOS::getArchitecture() { + struct utsname buf; + if (uname(&buf) == 0) { + std::string machine = buf.machine; + if (machine == "x86_64" || machine == "amd64") return "64-bit"; + if (machine == "i386" || machine == "i686") return "32-bit"; + if (machine == "aarch64") return "ARM64"; + if (machine == "armv7l") return "ARM32"; + return machine; + } + return "Unknown"; +} + +#elif PLATFORM_FREEBSD + +std::string CompactOS::getOSName() { + std::string ostype = Platform::sysctlString("kern.ostype"); + std::string release = Platform::sysctlString("kern.osrelease"); + + if (!ostype.empty()) { + size_t dash = release.find('-'); + if (dash != std::string::npos) { + release = release.substr(0, dash); + } + return ostype + " " + release; + } + return "FreeBSD"; +} + +std::string CompactOS::getOSBuild() { + std::string release = Platform::sysctlString("kern.osrelease"); + return release.empty() ? "Unknown" : release; +} + +std::string CompactOS::getUptime() { + struct timeval boottime; + size_t len = sizeof(boottime); + int mib[2] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(mib, 2, &boottime, &len, nullptr, 0) == 0) { + time_t now = time(nullptr); + time_t uptime_seconds = now - boottime.tv_sec; + + int days = static_cast(uptime_seconds / 86400); + int hours = static_cast((uptime_seconds % 86400) / 3600); + int minutes = static_cast((uptime_seconds % 3600) / 60); + + std::ostringstream oss; + if (days > 0) oss << days << "d "; + if (hours > 0) oss << hours << "h "; + oss << minutes << "m"; + return oss.str(); + } + return "Unknown"; +} + +std::string CompactOS::getArchitecture() { + std::string arch = Platform::sysctlString("hw.machine_arch"); + if (!arch.empty()) { + if (arch == "amd64" || arch == "x86_64") return "64-bit"; + if (arch == "i386" || arch == "i686") return "32-bit"; + if (arch == "aarch64" || arch == "arm64") return "ARM64"; + return arch; + } + return "Unknown"; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactPerformancePosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactPerformancePosix.cpp new file mode 100644 index 0000000..d0899fd --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactPerformancePosix.cpp @@ -0,0 +1,171 @@ +#include "CompactPerformance.h" +#include "platform/Platform.h" +#include +#include +#include +#include +#include + +#if PLATFORM_LINUX + +static long prev_idle = 0; +static long prev_total = 0; +static bool cpu_first_call = true; + +int CompactPerformance::getCPUUsage() { + std::string stat = Platform::readFileLine("/proc/stat"); + if (stat.empty()) return 0; + + long user, nice, system, idle, iowait, irq, softirq, steal; + if (sscanf(stat.c_str(), "cpu %ld %ld %ld %ld %ld %ld %ld %ld", + &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal) != 8) { + return 0; + } + + long idle_time = idle + iowait; + long total_time = user + nice + system + idle + iowait + irq + softirq + steal; + + if (cpu_first_call) { + prev_idle = idle_time; + prev_total = total_time; + cpu_first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return getCPUUsage(); + } + + long idle_delta = idle_time - prev_idle; + long total_delta = total_time - prev_total; + + prev_idle = idle_time; + prev_total = total_time; + + if (total_delta == 0) return 0; + return static_cast((1.0 - static_cast(idle_delta) / total_delta) * 100.0); +} + +int CompactPerformance::getRAMUsage() { + std::string content = Platform::readFile("/proc/meminfo"); + + long long memTotal = 0, memAvailable = 0; + std::string val = Platform::parseValue(content, "MemTotal"); + if (!val.empty()) memTotal = std::stoll(val); + + val = Platform::parseValue(content, "MemAvailable"); + if (!val.empty()) { + memAvailable = std::stoll(val); + } else { + long long memFree = 0, buffers = 0, cached = 0; + val = Platform::parseValue(content, "MemFree"); + if (!val.empty()) memFree = std::stoll(val); + val = Platform::parseValue(content, "Buffers"); + if (!val.empty()) buffers = std::stoll(val); + val = Platform::parseValue(content, "Cached"); + if (!val.empty()) cached = std::stoll(val); + memAvailable = memFree + buffers + cached; + } + + if (memTotal == 0) return 0; + return static_cast(((memTotal - memAvailable) * 100) / memTotal); +} + +int CompactPerformance::getDiskUsage() { + struct statvfs stat; + if (statvfs("/", &stat) != 0) return 0; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + unsigned long long available = stat.f_bavail * stat.f_frsize; + + if (total == 0) return 0; + return static_cast(((total - available) * 100) / total); +} + +int CompactPerformance::getGPUUsage() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) { + try { return std::stoi(result); } + catch (...) {} + } + } + + std::string busy = Platform::readFileLine("/sys/class/drm/card0/device/gpu_busy_percent"); + if (!busy.empty()) { + try { return std::stoi(Platform::trim(busy)); } + catch (...) {} + } + + return 0; +} + +#elif PLATFORM_FREEBSD + +static long prev_cp_time[5] = {0}; +static bool cpu_first_call = true; + +int CompactPerformance::getCPUUsage() { + long cp_time[5]; + size_t len = sizeof(cp_time); + + if (sysctlbyname("kern.cp_time", cp_time, &len, nullptr, 0) != 0) { + return 0; + } + + if (cpu_first_call) { + memcpy(prev_cp_time, cp_time, sizeof(cp_time)); + cpu_first_call = false; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return getCPUUsage(); + } + + long total_delta = 0; + for (int i = 0; i < 5; i++) { + total_delta += cp_time[i] - prev_cp_time[i]; + } + long idle_delta = cp_time[4] - prev_cp_time[4]; + + memcpy(prev_cp_time, cp_time, sizeof(cp_time)); + + if (total_delta == 0) return 0; + return static_cast((1.0 - static_cast(idle_delta) / total_delta) * 100.0); +} + +int CompactPerformance::getRAMUsage() { + unsigned long physmem = Platform::sysctlULong("hw.physmem"); + unsigned long pagesize = Platform::sysctlULong("hw.pagesize"); + if (pagesize == 0) pagesize = 4096; + + unsigned long free_count = Platform::sysctlULong("vm.stats.vm.v_free_count"); + unsigned long inactive = Platform::sysctlULong("vm.stats.vm.v_inactive_count"); + unsigned long cache = Platform::sysctlULong("vm.stats.vm.v_cache_count"); + + unsigned long available = (free_count + inactive + cache) * pagesize; + + if (physmem == 0) return 0; + return static_cast(((physmem - available) * 100) / physmem); +} + +int CompactPerformance::getDiskUsage() { + struct statvfs stat; + if (statvfs("/", &stat) != 0) return 0; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + unsigned long long available = stat.f_bavail * stat.f_frsize; + + if (total == 0) return 0; + return static_cast(((total - available) * 100) / total); +} + +int CompactPerformance::getGPUUsage() { + if (Platform::commandExists("nvidia-smi")) { + std::string result = Platform::exec("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null | head -1"); + result = Platform::trim(result); + if (!result.empty()) { + try { return std::stoi(result); } + catch (...) {} + } + } + return 0; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactScreenPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactScreenPosix.cpp new file mode 100644 index 0000000..65c81bd --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactScreenPosix.cpp @@ -0,0 +1,184 @@ +#include "../../CompactScreen.h" +#include "../Platform.h" +#include +#include +#include +#include +#include + +#if PLATFORM_POSIX + +CompactScreen::CompactScreen() { + refresh(); +} + +bool CompactScreen::refresh() { + screens.clear(); + return populateFromXrandr() || populateFromDRM(); +} + +bool CompactScreen::isNvidiaPresent() { + return Platform::commandExists("nvidia-smi"); +} + +bool CompactScreen::isAMDPresent() { + std::string vendor = Platform::readFileLine("/sys/class/drm/card0/device/vendor"); + return vendor.find("1002") != std::string::npos; +} + +std::string CompactScreen::scaleMultiplier(int scalePercent) { + if (scalePercent <= 100) return "1x"; + if (scalePercent <= 125) return "1.25x"; + if (scalePercent <= 150) return "1.5x"; + if (scalePercent <= 175) return "1.75x"; + if (scalePercent <= 200) return "2x"; + return std::to_string(scalePercent / 100) + "x"; +} + +int CompactScreen::computeUpscaleFactor(int currentWidth, int nativeWidth) { + if (nativeWidth <= 0 || currentWidth <= nativeWidth) return 1; + return currentWidth / nativeWidth; +} + +bool CompactScreen::populateFromXrandr() { + if (!Platform::commandExists("xrandr")) return false; + + std::string output = Platform::exec("xrandr --query 2>/dev/null"); + if (output.empty()) return false; + + std::istringstream iss(output); + std::string line; + ScreenInfo current; + bool hasScreen = false; + + while (std::getline(iss, line)) { + if (line.find(" connected") != std::string::npos) { + if (hasScreen && current.current_width > 0) { + screens.push_back(current); + } + current = ScreenInfo(); + hasScreen = true; + + size_t pos = line.find(" connected"); + if (pos != std::string::npos) { + current.name = Platform::trim(line.substr(0, pos)); + } + + int w = 0, h = 0, offX = 0, offY = 0; + size_t resStart = line.find(" connected"); + if (resStart != std::string::npos) { + std::string rest = line.substr(resStart + 10); + if (rest.find("primary") == 0) { + rest = rest.substr(7); + } + rest = Platform::trim(rest); + + if (sscanf(rest.c_str(), "%dx%d+%d+%d", &w, &h, &offX, &offY) >= 2) { + current.current_width = w; + current.current_height = h; + current.native_width = w; + current.native_height = h; + } + } + + current.scale_percent = 100; + current.scale_mul = "1x"; + current.upscale = "Off"; + current.refresh_rate = 60; + } + else if (hasScreen && line.find("*") != std::string::npos) { + std::string trimmed = Platform::trim(line); + size_t starPos = trimmed.find('*'); + if (starPos != std::string::npos && starPos > 0) { + size_t hzEnd = starPos; + size_t hzStart = hzEnd; + while (hzStart > 0 && (std::isdigit(trimmed[hzStart-1]) || trimmed[hzStart-1] == '.')) { + hzStart--; + } + + if (hzStart < hzEnd) { + std::string hzStr = trimmed.substr(hzStart, hzEnd - hzStart); + try { + current.refresh_rate = static_cast(std::round(std::stof(hzStr))); + } catch (...) { + current.refresh_rate = 60; + } + } + } + } + } + + if (hasScreen && current.current_width > 0) { + screens.push_back(current); + } + + return !screens.empty(); +} + +bool CompactScreen::populateFromDRM() { + DIR* dir = opendir("/sys/class/drm"); + if (!dir) return false; + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name.find("card") == 0 && name.find("-") != std::string::npos) { + std::string statusPath = "/sys/class/drm/" + name + "/status"; + std::string status = Platform::readFileLine(statusPath); + if (Platform::trim(status) == "connected") { + ScreenInfo info; + info.name = name; + + std::string modesPath = "/sys/class/drm/" + name + "/modes"; + std::string modes = Platform::readFileLine(modesPath); + if (!modes.empty()) { + int w = 0, h = 0; + if (sscanf(modes.c_str(), "%dx%d", &w, &h) == 2) { + info.current_width = w; + info.current_height = h; + info.native_width = w; + info.native_height = h; + } + } + + info.refresh_rate = 60; + info.scale_percent = 100; + info.scale_mul = "1x"; + info.upscale = "Off"; + + screens.push_back(info); + } + } + } + closedir(dir); + + return !screens.empty(); +} + +CompactScreen::EDIDInfo CompactScreen::parseEDID(const unsigned char* edid, size_t size) { + EDIDInfo info = {"Unknown", 0, 0, false}; + if (!edid || size < 128) return info; + if (edid[0] != 0x00 || edid[1] != 0xFF || edid[2] != 0xFF) return info; + + for (int i = 54; i <= 108; i += 18) { + if (edid[i] == 0 && edid[i+1] == 0 && edid[i+3] == 0xFC) { + char name[14] = {0}; + memcpy(name, &edid[i+5], 13); + for (int j = 0; j < 13; j++) { + if (name[j] == '\n' || name[j] == '\r') name[j] = '\0'; + } + info.friendlyName = Platform::trim(std::string(name)); + break; + } + } + + if (edid[56] && edid[59]) { + info.nativeWidth = ((edid[58] & 0xF0) << 4) | edid[56]; + info.nativeHeight = ((edid[61] & 0xF0) << 4) | edid[59]; + } + + info.valid = true; + return info; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactSystemPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactSystemPosix.cpp new file mode 100644 index 0000000..9cbdff3 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactSystemPosix.cpp @@ -0,0 +1,91 @@ +#include "CompactSystem.h" +#include "platform/Platform.h" + +#if PLATFORM_LINUX + +std::string CompactSystem::getBIOSInfo() { + std::string vendor = Platform::readFileLine("/sys/class/dmi/id/bios_vendor"); + std::string version = Platform::readFileLine("/sys/class/dmi/id/bios_version"); + + vendor = Platform::trim(vendor); + version = Platform::trim(version); + + if (!vendor.empty() && !version.empty()) { + return vendor + " " + version; + } + if (!vendor.empty()) return vendor; + if (!version.empty()) return version; + + if (Platform::commandExists("dmidecode")) { + std::string result = Platform::exec("sudo dmidecode -s bios-vendor 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty()) { + std::string ver = Platform::exec("sudo dmidecode -s bios-version 2>/dev/null"); + ver = Platform::trim(ver); + if (!ver.empty()) result += " " + ver; + return result; + } + } + + return "Unknown"; +} + +std::string CompactSystem::getMotherboardInfo() { + std::string product = Platform::readFileLine("/sys/class/dmi/id/board_name"); + std::string vendor = Platform::readFileLine("/sys/class/dmi/id/board_vendor"); + + product = Platform::trim(product); + vendor = Platform::trim(vendor); + + if (!product.empty() && !vendor.empty()) { + return vendor + " " + product; + } + if (!product.empty()) return product; + if (!vendor.empty()) return vendor; + + if (Platform::commandExists("dmidecode")) { + std::string result = Platform::exec("sudo dmidecode -s baseboard-product-name 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty()) return result; + } + + return "Unknown"; +} + +#elif PLATFORM_FREEBSD + +std::string CompactSystem::getBIOSInfo() { + if (Platform::commandExists("kenv")) { + std::string vendor = Platform::exec("kenv smbios.bios.vendor 2>/dev/null"); + std::string version = Platform::exec("kenv smbios.bios.version 2>/dev/null"); + + vendor = Platform::trim(vendor); + version = Platform::trim(version); + + if (!vendor.empty() && !version.empty()) { + return vendor + " " + version; + } + if (!vendor.empty()) return vendor; + if (!version.empty()) return version; + } + return "Unknown"; +} + +std::string CompactSystem::getMotherboardInfo() { + if (Platform::commandExists("kenv")) { + std::string product = Platform::exec("kenv smbios.planar.product 2>/dev/null"); + std::string maker = Platform::exec("kenv smbios.planar.maker 2>/dev/null"); + + product = Platform::trim(product); + maker = Platform::trim(maker); + + if (!product.empty() && !maker.empty()) { + return maker + " " + product; + } + if (!product.empty()) return product; + if (!maker.empty()) return maker; + } + return "Unknown"; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/CompactUserPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactUserPosix.cpp new file mode 100644 index 0000000..e59d880 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/CompactUserPosix.cpp @@ -0,0 +1,46 @@ +#include "CompactUser.h" +#include "platform/Platform.h" +#include +#include +#include + +#if PLATFORM_POSIX + +std::string CompactUser::getUsername() { + struct passwd* pw = getpwuid(getuid()); + if (pw && pw->pw_name) { + return std::string(pw->pw_name); + } + + const char* user = getenv("USER"); + if (user) return std::string(user); + + return "Unknown"; +} + +std::string CompactUser::getDomain() { + char hostname[HOST_NAME_MAX + 1] = {0}; + if (gethostname(hostname, sizeof(hostname) - 1) == 0) { + return std::string(hostname); + } + return "localhost"; +} + +std::string CompactUser::isAdmin() { + if (getuid() == 0 || geteuid() == 0) { + return "Root"; + } + + if (Platform::commandExists("sudo")) { + std::string groups = Platform::exec("groups 2>/dev/null"); + if (groups.find("sudo") != std::string::npos || + groups.find("wheel") != std::string::npos || + groups.find("admin") != std::string::npos) { + return "Sudoer"; + } + } + + return "User"; +} + +#endif diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/ConsoleUtilsPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/ConsoleUtilsPosix.cpp new file mode 100644 index 0000000..4262608 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/ConsoleUtilsPosix.cpp @@ -0,0 +1 @@ +// POSIX uses ANSI escape codes for console formatting - no special implementation needed diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/DetailedGPUInfoPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/DetailedGPUInfoPosix.cpp new file mode 100644 index 0000000..321f7a9 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/DetailedGPUInfoPosix.cpp @@ -0,0 +1,188 @@ +#include "../Platform.h" +#include "../../DetailedGPUInfo.h" +#include +#include +#include +#include + +using namespace std; +using namespace Platform; + +DetailedGPUInfo::DetailedGPUInfo() {} +DetailedGPUInfo::~DetailedGPUInfo() {} + +static vector get_nvidia_gpus() { + vector gpus; + + if (!commandExists("nvidia-smi")) { + return gpus; + } + + string output = exec("nvidia-smi --query-gpu=index,name,memory.total,clocks.gr --format=csv,noheader,nounits 2>/dev/null"); + if (output.empty()) return gpus; + + istringstream iss(output); + string line; + + while (getline(iss, line)) { + if (line.empty()) continue; + + vector parts = split(line, ','); + if (parts.size() < 3) continue; + + GPUData gpu; + + try { + gpu.index = stoi(trim(parts[0])); + } catch (...) { + gpu.index = static_cast(gpus.size()); + } + + gpu.name = trim(parts[1]); + + try { + float vramMiB = stof(trim(parts[2])); + gpu.vram_gb = vramMiB / 1024.0f; + } catch (...) { + gpu.vram_gb = 0.0f; + } + + if (parts.size() >= 4) { + try { + float clockMHz = stof(trim(parts[3])); + gpu.frequency_ghz = clockMHz / 1000.0f; + } catch (...) { + gpu.frequency_ghz = 0.0f; + } + } else { + gpu.frequency_ghz = 0.0f; + } + + gpus.push_back(gpu); + } + + return gpus; +} + +static vector get_lspci_gpus() { + vector gpus; + + if (!commandExists("lspci")) { + return gpus; + } + + string output = exec("lspci -nn 2>/dev/null | grep -iE 'VGA|3D|Display' 2>/dev/null"); + if (output.empty()) return gpus; + + istringstream iss(output); + string line; + int index = 0; + + while (getline(iss, line)) { + if (line.empty()) continue; + + GPUData gpu; + gpu.index = index++; + + size_t colonPos = line.find("]: "); + size_t bracketPos = line.rfind(" ["); + + if (colonPos != string::npos && bracketPos != string::npos && bracketPos > colonPos) { + gpu.name = trim(line.substr(colonPos + 3, bracketPos - colonPos - 3)); + } else if (colonPos != string::npos) { + gpu.name = trim(line.substr(colonPos + 3)); + } else { + gpu.name = trim(line); + } + + gpu.vram_gb = 0.0f; + gpu.frequency_ghz = 0.0f; + + gpus.push_back(gpu); + } + + return gpus; +} + +static void enrich_from_sysfs(vector& gpus) { + const string drmPath = "/sys/class/drm/"; + + DIR* dir = opendir(drmPath.c_str()); + if (!dir) return; + + struct dirent* entry; + int cardIndex = 0; + + while ((entry = readdir(dir)) != nullptr) { + string name = entry->d_name; + + if (name.find("card") != 0 || name.find('-') != string::npos) continue; + + string cardPath = drmPath + name + "/device/"; + + string vramStr = trim(readFile(cardPath + "mem_info_vram_total")); + if (!vramStr.empty() && cardIndex < static_cast(gpus.size())) { + try { + unsigned long long vramBytes = stoull(vramStr); + gpus[cardIndex].vram_gb = static_cast(vramBytes) / (1024.0f * 1024.0f * 1024.0f); + } catch (...) {} + } + + string ppDpmSclk = trim(readFile(cardPath + "pp_dpm_sclk")); + if (!ppDpmSclk.empty() && cardIndex < static_cast(gpus.size())) { + istringstream iss(ppDpmSclk); + string line; + while (getline(iss, line)) { + if (line.find('*') != string::npos) { + size_t mhzPos = line.find("Mhz"); + if (mhzPos == string::npos) mhzPos = line.find("MHz"); + if (mhzPos != string::npos) { + size_t colonPos = line.find(':'); + if (colonPos != string::npos) { + string clockStr = trim(line.substr(colonPos + 1, mhzPos - colonPos - 1)); + try { + float clockMHz = stof(clockStr); + gpus[cardIndex].frequency_ghz = clockMHz / 1000.0f; + } catch (...) {} + } + } + break; + } + } + } + + cardIndex++; + } + + closedir(dir); +} + +vector DetailedGPUInfo::get_all_gpus() { + vector gpus = get_nvidia_gpus(); + + if (gpus.empty()) { + gpus = get_lspci_gpus(); + } + + enrich_from_sysfs(gpus); + + if (gpus.empty()) { + GPUData gpu; + gpu.index = 0; + gpu.name = "Unknown GPU"; + gpu.vram_gb = 0.0f; + gpu.frequency_ghz = 0.0f; + gpus.push_back(gpu); + } + + return gpus; +} + +GPUData DetailedGPUInfo::primary_gpu_info() { + auto gpus = get_all_gpus(); + if (!gpus.empty()) { + return gpus[0]; + } + + return GPUData{ -1, "No GPU Found", 0.0f, 0.0f }; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/DetailedScreenPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/DetailedScreenPosix.cpp new file mode 100644 index 0000000..0a0f478 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/DetailedScreenPosix.cpp @@ -0,0 +1,270 @@ +#include "../Platform.h" +#include "../../DetailedScreen.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace Platform; + +DetailedScreen::DetailedScreen() { + refresh(); +} + +string DetailedScreen::scaleMultiplier(int scalePercent) { + if (scalePercent <= 100) return "1x"; + if (scalePercent <= 125) return "1.25x"; + if (scalePercent <= 150) return "1.5x"; + if (scalePercent <= 175) return "1.75x"; + if (scalePercent <= 200) return "2x"; + if (scalePercent <= 250) return "2.5x"; + if (scalePercent <= 300) return "3x"; + return to_string(scalePercent / 100) + "x"; +} + +int DetailedScreen::computeUpscaleFactor(int currentWidth, int nativeWidth) { + if (nativeWidth <= 0) return 1; + return (currentWidth + nativeWidth - 1) / nativeWidth; +} + +float DetailedScreen::calculatePPI(int width, int height, float diagonalInches) { + if (diagonalInches <= 0) return 0.0f; + float diagonalPixels = sqrt(static_cast(width * width + height * height)); + return diagonalPixels / diagonalInches; +} + +float DetailedScreen::calculateDiagonal(float widthMM, float heightMM) { + return sqrt(widthMM * widthMM + heightMM * heightMM); +} + +float DetailedScreen::calculateScreenSizeInches(float widthMM, float heightMM) { + float diagonalMM = calculateDiagonal(widthMM, heightMM); + return diagonalMM / 25.4f; +} + +bool DetailedScreen::isNvidiaPresent() { + return commandExists("nvidia-smi") && !exec("nvidia-smi -L 2>/dev/null").empty(); +} + +bool DetailedScreen::isAMDPresent() { + string output = exec("lspci 2>/dev/null | grep -i 'AMD\\|ATI\\|Radeon' 2>/dev/null"); + return !output.empty(); +} + +string DetailedScreen::getGPUVendor() { + if (isNvidiaPresent()) return "NVIDIA"; + if (isAMDPresent()) return "AMD"; + + string output = exec("lspci 2>/dev/null | grep -i 'Intel.*Graphics' 2>/dev/null"); + if (!output.empty()) return "Intel"; + + return "Unknown"; +} + +bool DetailedScreen::populateFromXrandr() { + if (!commandExists("xrandr")) { + return false; + } + + string output = exec("xrandr --query 2>/dev/null"); + if (output.empty()) return false; + + istringstream iss(output); + string line; + DetailedScreenInfo currentScreen; + bool inScreen = false; + + while (getline(iss, line)) { + if (line.find(" connected") != string::npos) { + if (inScreen && !currentScreen.name.empty()) { + screens.push_back(currentScreen); + } + + currentScreen = DetailedScreenInfo(); + inScreen = true; + + size_t spacePos = line.find(' '); + if (spacePos != string::npos) { + currentScreen.deviceName = line.substr(0, spacePos); + currentScreen.name = currentScreen.deviceName; + } + + currentScreen.isPrimary = (line.find("primary") != string::npos); + + size_t resStart = string::npos; + for (size_t i = 0; i < line.size(); i++) { + if (isdigit(line[i]) && (i == 0 || !isdigit(line[i-1]))) { + size_t xPos = line.find('x', i); + if (xPos != string::npos && xPos < i + 6) { + resStart = i; + break; + } + } + } + + if (resStart != string::npos) { + size_t xPos = line.find('x', resStart); + size_t plusPos = line.find('+', resStart); + + if (xPos != string::npos) { + try { + currentScreen.current_width = stoi(line.substr(resStart, xPos - resStart)); + + size_t heightEnd = (plusPos != string::npos) ? plusPos : line.find(' ', xPos); + if (heightEnd != string::npos) { + currentScreen.current_height = stoi(line.substr(xPos + 1, heightEnd - xPos - 1)); + } + } catch (...) {} + } + + if (plusPos != string::npos) { + size_t secondPlus = line.find('+', plusPos + 1); + if (secondPlus != string::npos) { + try { + currentScreen.pos_x = stoi(line.substr(plusPos + 1, secondPlus - plusPos - 1)); + size_t endPos = line.find(' ', secondPlus); + currentScreen.pos_y = stoi(line.substr(secondPlus + 1, endPos - secondPlus - 1)); + } catch (...) {} + } + } + } + + size_t mmPos = line.find("mm x "); + if (mmPos != string::npos) { + size_t widthStart = mmPos; + while (widthStart > 0 && (isdigit(line[widthStart-1]) || line[widthStart-1] == ' ')) { + widthStart--; + } + + try { + currentScreen.width_mm = stof(line.substr(widthStart, mmPos - widthStart)); + + size_t heightStart = mmPos + 5; + size_t heightEnd = line.find("mm", heightStart); + if (heightEnd != string::npos) { + currentScreen.height_mm = stof(line.substr(heightStart, heightEnd - heightStart)); + } + + currentScreen.diagonal_inches = calculateScreenSizeInches(currentScreen.width_mm, currentScreen.height_mm); + currentScreen.ppi = calculatePPI(currentScreen.current_width, currentScreen.current_height, currentScreen.diagonal_inches); + } catch (...) {} + } + + } else if (inScreen && line.find("*") != string::npos && line.find("x") != string::npos) { + size_t starPos = line.find('*'); + if (starPos != string::npos && starPos > 0) { + size_t rateStart = starPos - 1; + while (rateStart > 0 && (isdigit(line[rateStart-1]) || line[rateStart-1] == '.')) { + rateStart--; + } + + try { + float refreshFloat = stof(line.substr(rateStart, starPos - rateStart)); + currentScreen.refresh_rate = static_cast(round(refreshFloat)); + } catch (...) { + currentScreen.refresh_rate = 60; + } + } + + if (currentScreen.native_width == 0) { + size_t xPos = line.find('x'); + if (xPos != string::npos) { + size_t resStart = xPos; + while (resStart > 0 && isdigit(line[resStart-1])) { + resStart--; + } + + try { + currentScreen.native_width = stoi(line.substr(resStart, xPos - resStart)); + + size_t heightEnd = xPos + 1; + while (heightEnd < line.size() && isdigit(line[heightEnd])) { + heightEnd++; + } + currentScreen.native_height = stoi(line.substr(xPos + 1, heightEnd - xPos - 1)); + } catch (...) {} + } + } + } + } + + if (inScreen && !currentScreen.name.empty()) { + screens.push_back(currentScreen); + } + + return !screens.empty(); +} + +bool DetailedScreen::populateFromDRM() { + const string drmPath = "/sys/class/drm/"; + + DIR* dir = opendir(drmPath.c_str()); + if (!dir) return false; + + struct dirent* entry; + + while ((entry = readdir(dir)) != nullptr) { + string name = entry->d_name; + + if (name.find("card") != 0 || name.find('-') == string::npos) continue; + + string connectorPath = drmPath + name + "/"; + + string status = trim(readFile(connectorPath + "status")); + if (status != "connected") continue; + + DetailedScreenInfo screen; + screen.deviceName = name; + screen.name = name; + + string modes = readFile(connectorPath + "modes"); + if (!modes.empty()) { + istringstream iss(modes); + string mode; + if (getline(iss, mode)) { + size_t xPos = mode.find('x'); + if (xPos != string::npos) { + try { + screen.native_width = stoi(mode.substr(0, xPos)); + screen.native_height = stoi(mode.substr(xPos + 1)); + screen.current_width = screen.native_width; + screen.current_height = screen.native_height; + } catch (...) {} + } + } + } + + screens.push_back(screen); + } + + closedir(dir); + return !screens.empty(); +} + +bool DetailedScreen::refresh() { + screens.clear(); + + if (populateFromXrandr()) { + return true; + } + + if (populateFromDRM()) { + return true; + } + + DetailedScreenInfo placeholder; + placeholder.name = "Unknown Display"; + placeholder.deviceName = "Unknown"; + placeholder.isPrimary = true; + placeholder.current_width = 1920; + placeholder.current_height = 1080; + placeholder.native_width = 1920; + placeholder.native_height = 1080; + placeholder.refresh_rate = 60; + placeholder.scale_percent = 100; + screens.push_back(placeholder); + + return true; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/ExtraInfoPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/ExtraInfoPosix.cpp new file mode 100644 index 0000000..47b83f2 --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/ExtraInfoPosix.cpp @@ -0,0 +1,241 @@ +#include "../Platform.h" +#include "../../ExtraInfo.h" +#include +#include +#include +#include + +using namespace std; +using namespace Platform; + +static vector parse_pulseaudio_sinks() { + vector devices; + + string output = exec("pactl list sinks 2>/dev/null"); + if (output.empty()) return devices; + + istringstream iss(output); + string line; + AudioDevice current; + bool inSink = false; + string defaultSink = exec("pactl get-default-sink 2>/dev/null"); + defaultSink = trim(defaultSink); + + while (getline(iss, line)) { + if (line.find("Sink #") != string::npos) { + if (inSink && !current.name.empty()) { + devices.push_back(current); + } + current = AudioDevice(); + current.isOutput = true; + inSink = true; + } else if (inSink) { + if (line.find("Name:") != string::npos) { + size_t pos = line.find("Name:"); + string name = trim(line.substr(pos + 5)); + current.isActive = (name == defaultSink); + } else if (line.find("Description:") != string::npos) { + size_t pos = line.find("Description:"); + current.name = trim(line.substr(pos + 12)); + } + } + } + + if (inSink && !current.name.empty()) { + devices.push_back(current); + } + + return devices; +} + +static vector parse_pulseaudio_sources() { + vector devices; + + string output = exec("pactl list sources 2>/dev/null"); + if (output.empty()) return devices; + + istringstream iss(output); + string line; + AudioDevice current; + bool inSource = false; + string defaultSource = exec("pactl get-default-source 2>/dev/null"); + defaultSource = trim(defaultSource); + + while (getline(iss, line)) { + if (line.find("Source #") != string::npos) { + if (inSource && !current.name.empty()) { + if (current.name.find(".monitor") == string::npos && + current.name.find("Monitor") == string::npos) { + devices.push_back(current); + } + } + current = AudioDevice(); + current.isOutput = false; + inSource = true; + } else if (inSource) { + if (line.find("Name:") != string::npos) { + size_t pos = line.find("Name:"); + string name = trim(line.substr(pos + 5)); + current.isActive = (name == defaultSource); + } else if (line.find("Description:") != string::npos) { + size_t pos = line.find("Description:"); + current.name = trim(line.substr(pos + 12)); + } + } + } + + if (inSource && !current.name.empty()) { + if (current.name.find(".monitor") == string::npos && + current.name.find("Monitor") == string::npos) { + devices.push_back(current); + } + } + + return devices; +} + +static vector parse_alsa_outputs() { + vector devices; + + string output = exec("aplay -l 2>/dev/null"); + if (output.empty()) return devices; + + istringstream iss(output); + string line; + + while (getline(iss, line)) { + if (line.find("card") == 0 && line.find("device") != string::npos) { + AudioDevice dev; + dev.isOutput = true; + dev.isActive = (devices.empty()); + + size_t start = line.find('['); + size_t end = line.rfind(']'); + if (start != string::npos && end != string::npos && end > start) { + dev.name = line.substr(start + 1, end - start - 1); + } else { + dev.name = line; + } + + devices.push_back(dev); + } + } + + return devices; +} + +static vector parse_alsa_inputs() { + vector devices; + + string output = exec("arecord -l 2>/dev/null"); + if (output.empty()) return devices; + + istringstream iss(output); + string line; + + while (getline(iss, line)) { + if (line.find("card") == 0 && line.find("device") != string::npos) { + AudioDevice dev; + dev.isOutput = false; + dev.isActive = (devices.empty()); + + size_t start = line.find('['); + size_t end = line.rfind(']'); + if (start != string::npos && end != string::npos && end > start) { + dev.name = line.substr(start + 1, end - start - 1); + } else { + dev.name = line; + } + + devices.push_back(dev); + } + } + + return devices; +} + +vector ExtraInfo::get_output_devices() { + vector devices = parse_pulseaudio_sinks(); + + if (devices.empty()) { + devices = parse_alsa_outputs(); + } + + if (devices.empty()) { + AudioDevice dev; + dev.name = "Default Audio Output"; + dev.isActive = true; + dev.isOutput = true; + devices.push_back(dev); + } + + return devices; +} + +vector ExtraInfo::get_input_devices() { + vector devices = parse_pulseaudio_sources(); + + if (devices.empty()) { + devices = parse_alsa_inputs(); + } + + if (devices.empty()) { + AudioDevice dev; + dev.name = "Default Audio Input"; + dev.isActive = true; + dev.isOutput = false; + devices.push_back(dev); + } + + return devices; +} + +PowerStatus ExtraInfo::get_power_status() { + PowerStatus status; + status.hasBattery = false; + status.batteryPercent = 0; + status.isACOnline = true; + status.isCharging = false; + + const string powerSupplyPath = "/sys/class/power_supply/"; + + DIR* dir = opendir(powerSupplyPath.c_str()); + if (!dir) { + return status; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + string name = entry->d_name; + if (name == "." || name == "..") continue; + + string devicePath = powerSupplyPath + name + "/"; + string type = trim(readFile(devicePath + "type")); + + if (type == "Battery") { + status.hasBattery = true; + + string capacityStr = trim(readFile(devicePath + "capacity")); + if (!capacityStr.empty()) { + try { + status.batteryPercent = stoi(capacityStr); + } catch (...) { + status.batteryPercent = 0; + } + } + + string batteryStatus = trim(readFile(devicePath + "status")); + status.isCharging = (batteryStatus == "Charging"); + status.isACOnline = (batteryStatus == "Charging" || batteryStatus == "Full" || batteryStatus == "Not charging"); + + } else if (type == "Mains") { + string online = trim(readFile(devicePath + "online")); + if (online == "1") { + status.isACOnline = true; + } + } + } + + closedir(dir); + return status; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/UserInfoPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/UserInfoPosix.cpp new file mode 100644 index 0000000..21f2c1c --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/UserInfoPosix.cpp @@ -0,0 +1,78 @@ +#include "../Platform.h" +#include "../../UserInfo.h" +#include +#include +#include +#include +#include + +std::string UserInfo::get_username() { + const char* user = std::getenv("USER"); + if (user) return std::string(user); + + struct passwd* pw = getpwuid(getuid()); + if (pw) return std::string(pw->pw_name); + + return "Unknown User Name"; +} + +std::string UserInfo::get_domain_name() { + char hostname[256]; + if (gethostname(hostname, sizeof(hostname)) == 0) { + char* dot = strchr(hostname, '.'); + if (dot && *(dot + 1)) { + return std::string(dot + 1); + } + } + + std::string result = Platform::exec("hostname -d 2>/dev/null"); + result = Platform::trim(result); + if (!result.empty() && result != "(none)") { + return result; + } + + std::string resolv = Platform::readFile("/etc/resolv.conf"); + std::string search = Platform::parseValue(resolv, "search", ' '); + if (!search.empty()) { + auto parts = Platform::split(search, ' '); + if (!parts.empty()) return parts[0]; + } + + std::string domain = Platform::parseValue(resolv, "domain", ' '); + if (!domain.empty()) return domain; + + return "No Domain / Workgroup"; +} + +std::string UserInfo::get_user_groups() { + gid_t groups[64]; + int ngroups = 64; + + struct passwd* pw = getpwuid(getuid()); + if (!pw) return "Failed to retrieve groups"; + + if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) == -1) { + return "Failed to retrieve groups"; + } + + std::string result; + for (int i = 0; i < ngroups; i++) { + struct group* gr = getgrgid(groups[i]); + if (gr) { + if (!result.empty()) result += ", "; + result += gr->gr_name; + } + } + + return result.empty() ? "No Groups Found" : result; +} + +std::string UserInfo::get_computer_name() { + char hostname[256]; + if (gethostname(hostname, sizeof(hostname)) == 0) { + char* dot = strchr(hostname, '.'); + if (dot) *dot = '\0'; + return std::string(hostname); + } + return "Unknown System"; +} diff --git a/project_binary_fetch/binary_fetch_v1/platform/posix/compact_disk_infoPosix.cpp b/project_binary_fetch/binary_fetch_v1/platform/posix/compact_disk_infoPosix.cpp new file mode 100644 index 0000000..96d67ea --- /dev/null +++ b/project_binary_fetch/binary_fetch_v1/platform/posix/compact_disk_infoPosix.cpp @@ -0,0 +1,188 @@ +#include "compact_disk_info.h" +#include "platform/Platform.h" +#include +#include +#include +#include + +#if PLATFORM_LINUX + +DiskInfo::DiskInfo() {} + +std::vector> DiskInfo::getAllDiskUsage() { + std::vector> result; + std::set seen; + + std::ifstream mounts("/proc/mounts"); + std::string line; + + while (std::getline(mounts, line)) { + std::istringstream iss(line); + std::string device, mountpoint, fstype; + iss >> device >> mountpoint >> fstype; + + bool isRealFs = (device.find("/dev/") == 0) || + (fstype == "zfs") || + (fstype == "btrfs" && mountpoint != "/") || + (fstype == "ext4" || fstype == "ext3" || fstype == "xfs"); + + if (!isRealFs) continue; + if (fstype == "squashfs" || fstype == "tmpfs" || fstype == "devtmpfs") continue; + if (mountpoint.find("/sys") == 0 || mountpoint.find("/proc") == 0) continue; + if (mountpoint == "/nix/store") continue; + if (seen.count(mountpoint)) continue; + seen.insert(mountpoint); + + int usage = calculateUsedPercentage(mountpoint); + + std::string label = mountpoint; + if (mountpoint == "/") label = "/"; + else if (mountpoint.rfind('/') != std::string::npos) { + label = mountpoint.substr(mountpoint.rfind('/') + 1); + } + + result.push_back({label, usage}); + } + + return result; +} + +std::vector> DiskInfo::getDiskCapacity() { + std::vector> result; + std::set seen; + + std::ifstream mounts("/proc/mounts"); + std::string line; + + while (std::getline(mounts, line)) { + std::istringstream iss(line); + std::string device, mountpoint, fstype; + iss >> device >> mountpoint >> fstype; + + bool isRealFs = (device.find("/dev/") == 0) || + (fstype == "zfs") || + (fstype == "btrfs" && mountpoint != "/") || + (fstype == "ext4" || fstype == "ext3" || fstype == "xfs"); + + if (!isRealFs) continue; + if (fstype == "squashfs" || fstype == "tmpfs" || fstype == "devtmpfs") continue; + if (mountpoint.find("/sys") == 0 || mountpoint.find("/proc") == 0) continue; + if (mountpoint == "/nix/store") continue; + if (seen.count(mountpoint)) continue; + seen.insert(mountpoint); + + int capacity = calculateCapacityGB(mountpoint); + + std::string label = mountpoint; + if (mountpoint == "/") label = "/"; + else if (mountpoint.rfind('/') != std::string::npos) { + label = mountpoint.substr(mountpoint.rfind('/') + 1); + } + + result.push_back({label, capacity}); + } + + return result; +} + +int DiskInfo::calculateUsedPercentage(const std::string& path) { + struct statvfs stat; + if (statvfs(path.c_str(), &stat) != 0) return 0; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + unsigned long long available = stat.f_bavail * stat.f_frsize; + + if (total == 0) return 0; + return static_cast(((total - available) * 100) / total); +} + +int DiskInfo::calculateCapacityGB(const std::string& path) { + struct statvfs stat; + if (statvfs(path.c_str(), &stat) != 0) return 0; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + return static_cast(total / (1024ULL * 1024ULL * 1024ULL)); +} + +#elif PLATFORM_FREEBSD + +#include + +DiskInfo::DiskInfo() {} + +std::vector> DiskInfo::getAllDiskUsage() { + std::vector> result; + + struct statfs* mounts; + int count = getmntinfo(&mounts, MNT_NOWAIT); + + for (int i = 0; i < count; i++) { + std::string fstype = mounts[i].f_fstypename; + std::string device = mounts[i].f_mntfromname; + std::string mountpoint = mounts[i].f_mntonname; + + if (device.find("/dev/") != 0) continue; + if (fstype == "devfs" || fstype == "nullfs" || fstype == "tmpfs") continue; + + int usage = calculateUsedPercentage(mountpoint); + + std::string label = mountpoint; + if (mountpoint == "/") label = "/"; + else if (mountpoint.rfind('/') != std::string::npos) { + label = mountpoint.substr(mountpoint.rfind('/') + 1); + } + + result.push_back({label, usage}); + } + + return result; +} + +std::vector> DiskInfo::getDiskCapacity() { + std::vector> result; + + struct statfs* mounts; + int count = getmntinfo(&mounts, MNT_NOWAIT); + + for (int i = 0; i < count; i++) { + std::string fstype = mounts[i].f_fstypename; + std::string device = mounts[i].f_mntfromname; + std::string mountpoint = mounts[i].f_mntonname; + + if (device.find("/dev/") != 0) continue; + if (fstype == "devfs" || fstype == "nullfs" || fstype == "tmpfs") continue; + + int capacity = calculateCapacityGB(mountpoint); + + std::string label = mountpoint; + if (mountpoint == "/") label = "/"; + else if (mountpoint.rfind('/') != std::string::npos) { + label = mountpoint.substr(mountpoint.rfind('/') + 1); + } + + result.push_back({label, capacity}); + } + + return result; +} + +int DiskInfo::calculateUsedPercentage(const std::string& path) { + struct statvfs stat; + if (statvfs(path.c_str(), &stat) != 0) return 0; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + unsigned long long available = stat.f_bavail * stat.f_frsize; + + if (total == 0) return 0; + return static_cast(((total - available) * 100) / total); +} + +int DiskInfo::calculateCapacityGB(const std::string& path) { + struct statvfs stat; + if (statvfs(path.c_str(), &stat) != 0) return 0; + + unsigned long long total = stat.f_blocks * stat.f_frsize; + return static_cast(total / (1024ULL * 1024ULL * 1024ULL)); +} + +#endif