Mercurial > topfetch++
view main.cpp @ 3:f7c837561bfa default tip
color CPU by load
| author | Yasuri <yasuri@charliesmp.net> |
|---|---|
| date | Sun, 04 Oct 2026 14:59:57 -0500 |
| parents | 841975e638f2 |
| children |
line wrap: on
line source
// topfetch++ - live system monitor + fetch tool // ISC licensed, see LICENSE. // Logos adapted from pfetch (MIT), see THIRD-PARTY-NOTICES._ #include <algorithm> #include <chrono> #include <fstream> #include <iomanip> #include <iostream> #include <map> #include <sstream> #include <string> #include <thread> #include <vector> // ---------- simple readers ---------- std::string readHostname() { std::ifstream f("/proc/sys/kernel/hostname"); if (!f.is_open()) return "(none)"; std::string s; std::getline(f, s); return s.empty() ? "(none)" : s; } std::string readVersion() { std::ifstream f("/proc/version"); if (!f.is_open()) return "unknown"; std::string w1, w2, version; f >> w1 >> w2 >> version; // "Linux version 6.x.y ..." return version.empty() ? "unknown" : version; } long readUptime() { std::ifstream f("/proc/uptime"); if (!f.is_open()) return -1; double seconds = 0; f >> seconds; return static_cast<long>(seconds); } struct Mem { long total = 0; // kB long available = 0; // kB }; Mem readMeminfo() { Mem m; std::ifstream f("/proc/meminfo"); if (!f.is_open()) return m; std::string line; while (std::getline(f, line)) { std::istringstream ss(line); std::string name; long value = 0; ss >> name >> value; if (name == "MemTotal:") m.total = value; else if (name == "MemAvailable:") m.available = value; } return m; } struct CpuTimes { long busy = 0; long total = 0; }; CpuTimes readCpuTimes() { CpuTimes t; std::ifstream f("/proc/stat"); if (!f.is_open()) return t; std::string label; f >> label; // "cpu" long values[8] = {0}; // user nice system idle iowait irq softirq steal for (int i = 0; i < 8; i++) f >> values[i]; long total = 0; for (int i = 0; i < 8; i++) total += values[i]; long idle = values[3] + values[4]; t.total = total; t.busy = total - idle; return t; } // ---------- distro ---------- struct Distro { std::string pretty = "Linux"; std::string id; std::string idLike; }; std::string unquote(const std::string& v) { if (v.size() >= 2 && (v.front() == '"' || v.front() == '\'') && v.back() == v.front()) { return v.substr(1, v.size() - 2); } return v; } Distro readDistro() { Distro d; std::ifstream f("/etc/os-release"); if (!f.is_open()) return d; std::string line; while (std::getline(f, line)) { size_t pos = line.find('='); if (pos == std::string::npos) continue; std::string key = line.substr(0, pos); std::string val = unquote(line.substr(pos + 1)); if (key == "PRETTY_NAME" && !val.empty()) d.pretty = val; else if (key == "ID") d.id = val; else if (key == "ID_LIKE") d.idLike = val; } return d; } // ---------- logos ---------- struct Logo { std::vector<std::string> lines; std::string color; // ANSI escape }; // Logos adapted from pfetch (MIT) // Copyright (c) 2016-2021 Dylan Araps // Copyright (c) 2024-2025 Joey Reinhart Logo pickLogo(const Distro& d, const std::string& override = "") { static const std::map<std::string, Logo> logos = { {"nixos", {{ R"art( \\ \\ //)art", R"art( ==\\__\\/ //)art", R"art( // \\//)art", R"art(==// //==)art", R"art( //\\___//)art", R"art(// /\\ \\==)art", R"art( // \\ \\)art", }, "\033[34m"}}, {"arch", {{ R"art( /\)art", R"art( / \)art", R"art( /\ \)art", R"art( / \)art", R"art( / ,, \)art", R"art( / | | -\)art", R"art(/_-'' ''-_\)art", }, "\033[36m"}}, {"gentoo", {{ R"art( _-----_)art", R"art(( \)art", R"art(\ 0 \)art", R"art( \ ))art", R"art( / _/)art", R"art(( _-)art", R"art(\____-)art", }, "\033[35m"}}, }; static const Logo fallback = {{ R"art( .--.)art", R"art( |o_o |)art", R"art( |:_/ |)art", R"art( // \ \)art", R"art( (| | ))art", R"art(/'\_ _/`\)art", R"art(\___)=(___/)art", }, "\033[33m"}; std::string id = override.empty() ? d.id : override; auto it = logos.find(id); if (it != logos.end()) return it->second; // ID_LIKE fallback (Manjaro -> arch, etc.) std::istringstream ss(d.idLike); std::string word; while (ss >> word) { it = logos.find(word); if (it != logos.end()) return it->second; } return fallback; } // ---------- main ---------- int main(int argc, char** argv) { std::string override; if (argc >= 3 && std::string(argv[1]) == "--logo") override = argv[2]; const Distro distro = readDistro(); const Logo logo = pickLogo(distro, override); const std::string hostname = readHostname(); const std::string kernver = readVersion(); size_t logoWidth = 0; for (const auto& line : logo.lines) logoWidth = std::max(logoWidth, line.size()); CpuTimes prev = readCpuTimes(); const double kbToGb = 1024.0 * 1024.0; while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); Mem m = readMeminfo(); long up = readUptime(); CpuTimes now = readCpuTimes(); long totalChange = now.total - prev.total; double cpuPercent = 0.0; if (totalChange > 0) cpuPercent = 100.0 * (now.busy - prev.busy) / totalChange; prev = now; std::string cpuColor; if (cpuPercent >= 80) { cpuColor = "\033[31m"; } else if (cpuPercent >= 50) { cpuColor = "\033[33m"; } else { cpuColor = "\033[32m"; } std::ostringstream uptimeLine, memLine, cpuLine; uptimeLine << "Uptime: "; if (up < 0) { uptimeLine << "Unknown"; } else { uptimeLine << up / 86400 << "d " << (up % 86400) / 3600 << "h " << (up % 3600) / 60 << "m"; } memLine << std::fixed << std::setprecision(2) << "Memory: "; if (m.total == 0) { memLine << "Unknown"; } else { memLine << (m.total - m.available) / kbToGb << " GB / " << m.total / kbToGb << " GB"; } cpuLine << std::fixed << std::setprecision(1) << "CPU: " << cpuColor << cpuPercent << " %"; std::vector<std::string> info = { "OS: " + distro.pretty, "Hostname: " + hostname, "Kernel: " + kernver, uptimeLine.str(), memLine.str(), cpuLine.str(), }; size_t rows = std::max(logo.lines.size(), info.size()); std::cout << "\033[H\033[2J"; for (size_t i = 0; i < rows; i++) { std::string art = i < logo.lines.size() ? logo.lines[i] : ""; std::string text = i < info.size() ? info[i] : ""; std::cout << logo.color << std::left << std::setw(logoWidth) << art << "\033[0m" << " " << text << '\n'; } std::cout << std::flush; } }
