#include "display/ui/hostblock/HostBlock.h" #include #include #include "display/ui/bar/Orientation.h" #include "display/ui/bar/Style.h" #include "display/ui/theme/Theme.h" #include "display/ui/text/Helpers.h" #include "display/graphics/Color.h" namespace display::ui::hostblock { HostBlock::HostBlock(config::server::StyleConfig &config) : config(config), cpuBar( config.hostblock.cpu.width, config.hostblock.cpu.height, display::ui::bar::Orientation::Vertical, display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 180, 0}, display::graphics::Color{80, 80, 80}, true}), cpuTempBar( config.hostblock.cpu.temperature.width, config.hostblock.cpu.height, display::ui::bar::Orientation::Vertical, display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 180, 0}, display::graphics::Color{80, 80, 80}, true}), memBar( config.hostblock.width - config.hostblock.padding * 2, config.hostblock.memory.height, display::ui::bar::Orientation::Horizontal, display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 120, 200}, display::graphics::Color{80, 80, 80}, true}) { } void HostBlock::draw( display::graphics::Framebuffer &fb, display::ui::text::Renderer &text, int x, int y, const std::string &hostname, const metrics::Host &metrics) { bool online = std::chrono::high_resolution_clock::now() - metrics.packetTimepoint < std::chrono::seconds(5); // ===== Block background ===== fb.fillRect(x, y, config.hostblock.width, config.hostblock.height, display::ui::theme::hostblock::BACKGROUND); fb.drawRect(x, y, config.hostblock.width, config.hostblock.height, display::ui::theme::hostblock::BORDER); int cursorY = y + config.hostblock.padding; // ===== Header ===== fb.fillRect( x + config.hostblock.padding, cursorY, config.hostblock.width - config.hostblock.padding * 2, config.hostblock.header.height, online ? display::ui::theme::hostblock::HEADER : display::graphics::Red()); text.drawTextOutlined( fb, x + config.hostblock.padding + 2, cursorY + config.hostblock.header.height - 1, hostname, display::ui::theme::text::TEXT, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.header.font.name, config.hostblock.header.font.size}); cursorY += config.hostblock.header.height + config.hostblock.gap; // ===== CPU bars ===== int cpuCount = std::min(metrics.cpu.coreLoads.size(), config.hostblock.cpu.max_per_row * config.hostblock.cpu.rows); for (int i = 0; i < cpuCount; ++i) { int row = i / config.hostblock.cpu.max_per_row; int col = i % config.hostblock.cpu.max_per_row; int bx = x + config.hostblock.padding + col * (config.hostblock.cpu.width + config.hostblock.cpu.temperature.width - 1 + config.hostblock.cpu.gap_h); int by = cursorY + row * (config.hostblock.cpu.height + config.hostblock.cpu.gap_v); float value = std::clamp(metrics.cpu.coreLoads[i] / 100.0f, 0.0f, 1.0f); cpuBar.draw(fb, bx, by, value, 0, online); value = std::clamp((metrics.cpu.coreTemps[i].current - config.hostblock.cpu.temperature.min) / ((metrics.cpu.coreTemps[i].max == 0 ? static_cast(config.hostblock.cpu.temperature.max) : metrics.cpu.coreTemps[i].max) - config.hostblock.cpu.temperature.min), 0.0f, 1.0f); cpuTempBar.draw(fb, bx + config.hostblock.cpu.width - 1, by, value, 0, online); } cursorY += config.hostblock.cpu.rows * config.hostblock.cpu.height + (config.hostblock.cpu.rows - 1) * config.hostblock.cpu.gap_v + config.hostblock.gap; // ===== Memory bar ===== float memValue, swapValue = 0.0f, hugepagesValue = 0.0f; if (metrics.memory.memory.total > 0) memValue = static_cast(metrics.memory.memory.used) / static_cast(metrics.memory.memory.total); if (metrics.memory.hugepages.total > 0) hugepagesValue = static_cast(metrics.memory.hugepages.total) / static_cast(metrics.memory.memory.total); memBar.draw( fb, x + config.hostblock.padding, cursorY, std::clamp(memValue, 0.0f, 1.0f), std::clamp(hugepagesValue, 0.0f, 1.0f), online); text.drawTextOutlined(fb, x + config.hostblock.memory.font.padding, cursorY + 8, "MEM: " + display::ui::text::formatFloat(static_cast(metrics.memory.memory.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast(metrics.memory.memory.total) / 1073741824), display::ui::theme::text::TEXT, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.memory.font.name, config.hostblock.memory.font.size}); cursorY += config.hostblock.memory.height + config.hostblock.memory.gap; if (metrics.memory.swap.total > 0) { swapValue = static_cast(metrics.memory.swap.used) / static_cast(metrics.memory.swap.total); memBar.draw( fb, x + config.hostblock.padding, cursorY, std::clamp(swapValue, 0.0f, 1.0f), 0, online); text.drawTextOutlined(fb, x + config.hostblock.memory.font.padding, cursorY + 8, "SWP: " + display::ui::text::formatFloat(static_cast(metrics.memory.swap.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast(metrics.memory.swap.total) / 1073741824), display::ui::theme::text::TEXT, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.memory.font.name, config.hostblock.memory.font.size}); cursorY += config.hostblock.memory.height + config.hostblock.gap; } // ===== Disk bar ===== int diskCount = metrics.disks.size(); for (int i = 0; i < diskCount; ++i) { int by = cursorY + i * (config.hostblock.disks.height + config.hostblock.disks.gap); float value = std::clamp(static_cast(metrics.disks[i].metrics.used) / metrics.disks[i].metrics.total, 0.0f, 1.0f); memBar.draw(fb, x + config.hostblock.padding, by, value, 0, online); text.drawTextOutlined(fb, x + config.hostblock.disks.font.padding, by + 8, "D/" + metrics.disks[i].name + ": " + display::ui::text::formatFloat(static_cast(metrics.disks[i].metrics.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast(metrics.disks[i].metrics.total) / 1073741824), display::ui::theme::text::TEXT, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.disks.font.name, config.hostblock.disks.font.size}); } cursorY += diskCount * (config.hostblock.disks.height + config.hostblock.disks.gap); // ===== Network bar ===== for (const auto &iface : metrics.networks) { int by = cursorY; float rxNorm = 0.0f; float txNorm = 0.0f; if (iface.online) { if (iface.rxMaxBps > 0) rxNorm = std::clamp(static_cast(iface.rxBps / iface.rxMaxBps), 0.0f, 1.0f); if (iface.txMaxBps > 0) txNorm = std::clamp(static_cast(iface.txBps / iface.txMaxBps), 0.0f, 1.0f); } // background bar memBar.draw( fb, x + config.hostblock.padding, by, 0.0f, 0.0f, iface.online && online); int barX = x + config.hostblock.padding; int barW = config.hostblock.width - config.hostblock.padding * 2; int barH = config.hostblock.networks.height; // RX overlay fb.fillRect( barX, by, static_cast(barW * rxNorm), barH, {0, 180, 0}); // TX overlay fb.fillRect( barX, by, static_cast(barW * txNorm), barH, {200, 200, 0}); // border fb.drawRect(barX, by, barW, barH, display::ui::theme::hostblock::BORDER); // interface name + speeds std::string label = iface.name + " " + display::ui::text::formatSpeed(iface.rxBps) + " ↓ " + display::ui::text::formatSpeed(iface.txBps) + " ↑"; text.drawTextOutlined( fb, barX + config.hostblock.networks.font.padding, by + barH - 1, label, display::ui::theme::text::TEXT, display::ui::theme::text::OUTLINE, display::ui::text::Font{ config.hostblock.networks.font.name, config.hostblock.networks.font.size}); cursorY += config.hostblock.networks.height + config.hostblock.networks.gap; } } }