From 85295ae4d8b39063201dceaf0ecd9aefe8883bcb Mon Sep 17 00:00:00 2001 From: Eugene Lykov Date: Sun, 28 Dec 2025 22:28:26 +0000 Subject: [PATCH] Add correct fonts --- src/Display/Graphics/CMakeLists.txt | 1 + src/Display/Graphics/Renderer.cpp | 4 +- src/Display/Graphics/Renderer.h | 8 +- src/Display/UI/Bar/Style.h | 7 +- src/Display/UI/CMakeLists.txt | 4 +- src/Display/UI/Header/CMakeLists.txt | 18 ++++ src/Display/UI/Header/Header.cpp | 58 ++++++++++++ src/Display/UI/Header/Header.h | 36 ++++++++ src/Display/UI/HostBlock/HostBlock.cpp | 75 +++++++++++++--- src/Display/UI/HostBlock/HostBlock.h | 36 ++++---- src/Display/UI/Text/BitmapFont.cpp | 36 -------- src/Display/UI/Text/BitmapFont.h | 13 --- src/Display/UI/Text/CMakeLists.txt | 6 +- src/Display/UI/Text/Font.h | 24 +++++ src/Display/UI/Text/FontFace.cpp | 38 -------- src/Display/UI/Text/FontFace.h | 25 ------ src/Display/UI/Text/Fonts.cpp | 119 +++++++++++++++++++++++++ src/Display/UI/Text/Fonts.h | 28 ++++++ src/Display/UI/Text/GlyphCache.cpp | 35 -------- src/Display/UI/Text/GlyphCache.h | 28 ------ src/Display/UI/Text/GlyphKey.h | 28 ++++++ src/Display/UI/Text/Helpers.cpp | 20 +++++ src/Display/UI/Text/Helpers.h | 9 ++ src/Display/UI/Text/Renderer.cpp | 49 ++++++++-- src/Display/UI/Text/Renderer.h | 17 ++-- src/Display/UI/Theme/CMakeLists.txt | 15 ++++ src/Display/UI/Theme/Theme.h | 35 ++++++++ src/Helpers/CMakeLists.txt | 4 +- src/Helpers/Paths.cpp | 0 src/Helpers/Paths.h | 37 ++++++++ src/Metrics/Collector.cpp | 37 +++++--- src/Metrics/Host.cpp | 12 ++- src/Metrics/Memory.h | 9 +- src/Model/HostRegistry.cpp | 8 +- src/Model/HostRegistry.h | 4 +- 35 files changed, 626 insertions(+), 257 deletions(-) create mode 100644 src/Display/UI/Header/CMakeLists.txt create mode 100644 src/Display/UI/Header/Header.cpp create mode 100644 src/Display/UI/Header/Header.h delete mode 100644 src/Display/UI/Text/BitmapFont.cpp delete mode 100644 src/Display/UI/Text/BitmapFont.h create mode 100644 src/Display/UI/Text/Font.h delete mode 100644 src/Display/UI/Text/FontFace.cpp delete mode 100644 src/Display/UI/Text/FontFace.h create mode 100644 src/Display/UI/Text/Fonts.cpp create mode 100644 src/Display/UI/Text/Fonts.h delete mode 100644 src/Display/UI/Text/GlyphCache.cpp delete mode 100644 src/Display/UI/Text/GlyphCache.h create mode 100644 src/Display/UI/Text/GlyphKey.h create mode 100644 src/Display/UI/Text/Helpers.cpp create mode 100644 src/Display/UI/Text/Helpers.h create mode 100644 src/Display/UI/Theme/CMakeLists.txt create mode 100644 src/Display/UI/Theme/Theme.h create mode 100644 src/Helpers/Paths.cpp create mode 100644 src/Helpers/Paths.h diff --git a/src/Display/Graphics/CMakeLists.txt b/src/Display/Graphics/CMakeLists.txt index c073f7f..262fe7b 100644 --- a/src/Display/Graphics/CMakeLists.txt +++ b/src/Display/Graphics/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(DisplayGraphics Helpers::All Display::UI::Text Display::UI::HostBlock + Display::UI::Header ) target_include_directories(DisplayGraphics diff --git a/src/Display/Graphics/Renderer.cpp b/src/Display/Graphics/Renderer.cpp index 4f93f29..e3cd745 100644 --- a/src/Display/Graphics/Renderer.cpp +++ b/src/Display/Graphics/Renderer.cpp @@ -12,6 +12,8 @@ namespace Display::Graphics { framebuffer.clear(Color{0, 0, 0}); + header.draw(framebuffer, textRenderer, START_X, START_Y); + int blocksPerRow = (SCREEN_WIDTH + BLOCK_GAP) / (Display::UI::HostBlock::BLOCK_WIDTH + BLOCK_GAP); if (blocksPerRow < 1) @@ -25,7 +27,7 @@ namespace Display::Graphics int row = index / blocksPerRow; int x = START_X + col * (Display::UI::HostBlock::BLOCK_WIDTH + BLOCK_GAP); - int y = START_Y + row * (Display::UI::HostBlock::BLOCK_HEIGHT + BLOCK_GAP); + int y = START_Y + header.height() + BLOCK_GAP + row * (Display::UI::HostBlock::BLOCK_HEIGHT + BLOCK_GAP); hostblock.draw( framebuffer, diff --git a/src/Display/Graphics/Renderer.h b/src/Display/Graphics/Renderer.h index 129b912..137fc06 100644 --- a/src/Display/Graphics/Renderer.h +++ b/src/Display/Graphics/Renderer.h @@ -2,15 +2,16 @@ #include "Display/Graphics/Framebuffer.h" #include "Model/HostRegistry.h" #include "Display/UI/Text/Renderer.h" +#include "Display/UI/Header/Header.h" #include "Display/UI/HostBlock/HostBlock.h" #include "Display/Graphics/Color.h" #include namespace Display::Graphics { - static constexpr int START_X = 0; - static constexpr int START_Y = 10; - static constexpr int BLOCK_GAP = 4; + static constexpr int START_X = 0; // 0 + static constexpr int START_Y = 0; // 20 + static constexpr int BLOCK_GAP = 4; // 4 static constexpr int SCREEN_WIDTH = 240; @@ -28,6 +29,7 @@ namespace Display::Graphics Framebuffer &framebuffer; Model::HostRegistry ®istry; Display::UI::Text::Renderer textRenderer; + Display::UI::Header::Header header; Display::UI::HostBlock::HostBlock hostblock; }; } // namespace Display::Graphics diff --git a/src/Display/UI/Bar/Style.h b/src/Display/UI/Bar/Style.h index 643065c..1c0c08e 100644 --- a/src/Display/UI/Bar/Style.h +++ b/src/Display/UI/Bar/Style.h @@ -1,6 +1,7 @@ #pragma once #include "Display/Graphics/Color.h" +#include "Display/UI/Theme/Theme.h" namespace Display::UI::Bar { @@ -12,9 +13,9 @@ namespace Display::UI::Bar bool drawBorder; Style( - Display::Graphics::Color background = Display::Graphics::Color{30, 30, 30}, - Display::Graphics::Color fill = Display::Graphics::Color{0, 180, 0}, - Display::Graphics::Color border = Display::Graphics::Color{80, 80, 80}, + Display::Graphics::Color background = Display::UI::Theme::Bar::BACKGROUND, + Display::Graphics::Color fill = Display::UI::Theme::Bar::FILL, + Display::Graphics::Color border = Display::UI::Theme::Bar::BORDER, bool drawBorder = true) : background(background), fill(fill), diff --git a/src/Display/UI/CMakeLists.txt b/src/Display/UI/CMakeLists.txt index 9b68d2b..bb379ad 100644 --- a/src/Display/UI/CMakeLists.txt +++ b/src/Display/UI/CMakeLists.txt @@ -2,4 +2,6 @@ message(STATUS "··Configuring UI") add_subdirectory(Bar) add_subdirectory(Text) -add_subdirectory(HostBlock) \ No newline at end of file +add_subdirectory(Header) +add_subdirectory(HostBlock) +add_subdirectory(Theme) \ No newline at end of file diff --git a/src/Display/UI/Header/CMakeLists.txt b/src/Display/UI/Header/CMakeLists.txt new file mode 100644 index 0000000..2513e2f --- /dev/null +++ b/src/Display/UI/Header/CMakeLists.txt @@ -0,0 +1,18 @@ +message(STATUS "···Configuring Header") + +add_library(DisplayUIHeader + Header.cpp +) + +add_library(Display::UI::Header ALIAS DisplayUIHeader) + +target_link_libraries(DisplayUIHeader + PUBLIC + Display::Graphics + Display::UI::Text +) + +target_include_directories(DisplayUIHeader + PUBLIC + ${INCLUDE_BASE_DIR} +) diff --git a/src/Display/UI/Header/Header.cpp b/src/Display/UI/Header/Header.cpp new file mode 100644 index 0000000..7e2c3e3 --- /dev/null +++ b/src/Display/UI/Header/Header.cpp @@ -0,0 +1,58 @@ +#include "Display/UI/Header/Header.h" + +#include +#include +#include +#include +#include + +#include "Display/UI/Bar/Orientation.h" +#include "Display/UI/Bar/Style.h" +#include "Display/UI/Theme/Theme.h" +#include "Display/Graphics/Color.h" + +namespace Display::UI::Header +{ + Header::Header() + { + } + + void Header::draw( + Graphics::Framebuffer &fb, + UI::Text::Renderer &text, + int x, int y) + { + // ===== Block background ===== + fb.fillRect(x, y, HEADER_WIDTH, HEADER_HEIGHT, Display::UI::Theme::Header::BACKGROUND); + fb.drawRect(x, y, HEADER_WIDTH, HEADER_HEIGHT, Display::UI::Theme::Header::BORDER); + + int cursorY = y + PADDING; + + text.drawTextOutlined( + fb, + x + PADDING + 120 - 2, + cursorY + 8, + getCurrentDateTime(), + Display::UI::Theme::Text::TEXT, + Display::UI::Theme::Text::OUTLINE, + Display::UI::Text::Font{std::string(HEADER_FONT_NAME.begin(), HEADER_FONT_NAME.end()), HEADER_FONT_SIZE}); + } + + std::string Header::getCurrentDateTime() + { + auto now = std::chrono::system_clock::now(); + std::time_t t = std::chrono::system_clock::to_time_t(now); + std::tm tm{}; + localtime_r(&t, &tm); + + std::ostringstream oss; + oss << std::setfill('0') + << std::setw(2) << tm.tm_mday << '/' + << std::setw(2) << tm.tm_mon + 1 << '/' + << tm.tm_year + 1900 << ' ' + << std::setw(2) << tm.tm_hour << ':' + << std::setw(2) << tm.tm_min << ':' + << std::setw(2) << tm.tm_sec; + return oss.str(); + } +} diff --git a/src/Display/UI/Header/Header.h b/src/Display/UI/Header/Header.h new file mode 100644 index 0000000..766f641 --- /dev/null +++ b/src/Display/UI/Header/Header.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include "Display/Graphics/Framebuffer.h" +#include "Display/Graphics/Color.h" +#include "Display/UI/Text/Renderer.h" + +namespace Display::UI::Header +{ + // ===== Layout ===== + constexpr int HEADER_WIDTH = 240; // 116 + constexpr int HEADER_HEIGHT = 16; // 116 + constexpr int PADDING = 4; // 4 + constexpr std::string_view HEADER_FONT_NAME = "Pixel10"; + constexpr int HEADER_FONT_SIZE = 14; // 12 + + class Header + { + public: + Header(); + + static constexpr int width() { return HEADER_WIDTH; } + static constexpr int height() { return HEADER_HEIGHT; } + + void draw( + Graphics::Framebuffer &fb, + UI::Text::Renderer &text, + int x, int y); + + private: + std::string getCurrentDateTime(); + }; + +} diff --git a/src/Display/UI/HostBlock/HostBlock.cpp b/src/Display/UI/HostBlock/HostBlock.cpp index e733a1f..e6ede0f 100644 --- a/src/Display/UI/HostBlock/HostBlock.cpp +++ b/src/Display/UI/HostBlock/HostBlock.cpp @@ -4,7 +4,9 @@ #include "Display/UI/Bar/Orientation.h" #include "Display/UI/Bar/Style.h" +#include "Display/UI/Theme/Theme.h" #include "Display/Graphics/Color.h" +#include "Display/UI/Text/Helpers.h" namespace Display::UI::HostBlock { @@ -19,6 +21,7 @@ namespace Display::UI::HostBlock MEM_BAR_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}) + { } @@ -30,8 +33,8 @@ namespace Display::UI::HostBlock const Metrics::Host &metrics) { // ===== Block background ===== - fb.fillRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, BG_COLOR); - fb.drawRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, BORDER_COLOR); + fb.fillRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, Display::UI::Theme::HostBlock::BACKGROUND); + fb.drawRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, Display::UI::Theme::HostBlock::BORDER); int cursorY = y + PADDING; @@ -41,14 +44,18 @@ namespace Display::UI::HostBlock cursorY, BLOCK_WIDTH - PADDING * 2, HEADER_HEIGHT, - HEADER_BG); + Display::UI::Theme::HostBlock::HEADER); - text.drawText( + text.drawTextOutlined( fb, x + PADDING + 2, cursorY + HEADER_HEIGHT - 3, hostname, - TEXT_COLOR); + Display::UI::Theme::Text::TEXT, + Display::UI::Theme::Text::OUTLINE, + // Display::UI::Text::Font{"LiberationSans-Regular", HEADER_FONT_SIZE}); + // Display::UI::Text::Font{"PixelFive-Regular", 5}); + Display::UI::Text::Font{std::string(HEADER_FONT_NAME.begin(), HEADER_FONT_NAME.end()), HEADER_FONT_SIZE}); cursorY += HEADER_HEIGHT + SECTION_GAP; @@ -77,17 +84,63 @@ namespace Display::UI::HostBlock SECTION_GAP; // ===== Memory bar ===== - float memValue = 0.0f; - if (metrics.memory.total > 0) - { - memValue = float(metrics.memory.used) / - float(metrics.memory.total); - } + float memValue, swapValue = 0.0f; + if (metrics.memory.mem_total > 0) + memValue = metrics.memory.mem_used / metrics.memory.mem_total; memBar.draw( fb, x + PADDING, cursorY, std::clamp(memValue, 0.0f, 1.0f)); + text.drawTextOutlined(fb, + x + MEM_BAR_TEXT_PADDING_X, + cursorY + 8, + "M: " + Display::UI::Text::formatFloat(metrics.memory.mem_used / 1073741824) + "/" + Display::UI::Text::formatFloat(metrics.memory.mem_total / 1073741824), + Display::UI::Theme::Text::TEXT, + Display::UI::Theme::Text::OUTLINE, + Display::UI::Text::Font{"PixelFive-Regular", 5}); + + cursorY += MEM_BAR_HEIGHT + SECTION_GAP; + + if (metrics.memory.swap_total > 0) + { + swapValue = metrics.memory.swap_used / metrics.memory.swap_total; + + memBar.draw( + fb, + x + PADDING, + cursorY, + std::clamp(swapValue, 0.0f, 1.0f)); + text.drawTextOutlined(fb, + x + MEM_BAR_TEXT_PADDING_X, + cursorY + 8, + "S: " + Display::UI::Text::formatFloat(metrics.memory.swap_used / 1073741824) + "/" + Display::UI::Text::formatFloat(metrics.memory.swap_total / 1073741824), + Display::UI::Theme::Text::TEXT, + Display::UI::Theme::Text::OUTLINE, + Display::UI::Text::Font{"PixelFive-Regular", 5}); + + cursorY += MEM_BAR_HEIGHT + SECTION_GAP; + } + + // ===== Disk bar ===== + int diskCount = metrics.disks.size(); + + for (int i = 0; i < diskCount; ++i) + { + int by = cursorY + + i * (MEM_BAR_HEIGHT + SECTION_GAP); + + float value = std::clamp(metrics.disks[i].used / metrics.disks[i].total, 0.0f, 1.0f); + + memBar.draw(fb, x + PADDING, by, value); + text.drawTextOutlined(fb, + x + MEM_BAR_TEXT_PADDING_X, + by + 8, + metrics.disks[i].name + ": " + Display::UI::Text::formatFloat(metrics.disks[i].used / 1073741824) + "/" + Display::UI::Text::formatFloat(metrics.disks[i].total / 1073741824), + Display::UI::Theme::Text::TEXT, + Display::UI::Theme::Text::OUTLINE, + Display::UI::Text::Font{"PixelFive-Regular", 5}); + } } } diff --git a/src/Display/UI/HostBlock/HostBlock.h b/src/Display/UI/HostBlock/HostBlock.h index 5e6a1c1..9b949a9 100644 --- a/src/Display/UI/HostBlock/HostBlock.h +++ b/src/Display/UI/HostBlock/HostBlock.h @@ -12,37 +12,33 @@ namespace Display::UI::HostBlock { // ===== Layout ===== - constexpr int BLOCK_WIDTH = 116; - constexpr int BLOCK_HEIGHT = 116; - constexpr int PADDING = 4; + constexpr int BLOCK_WIDTH = 118; // 118 + constexpr int BLOCK_HEIGHT = 146; // 146 + constexpr int PADDING = 5; // 5 - constexpr int HEADER_HEIGHT = 16; - constexpr int SECTION_GAP = 4; + constexpr int HEADER_HEIGHT = 12; // 12 + constexpr std::string_view HEADER_FONT_NAME = "PixelFive-Regular"; + constexpr int HEADER_FONT_SIZE = 5; // 10 + constexpr int SECTION_GAP = 4; // 4 // ===== CPU bars ===== - constexpr int CPU_BAR_WIDTH = 10; - constexpr int CPU_BAR_GAP = 4; - constexpr int CPU_BAR_HEIGHT = 36; - // constexpr int CPU_BAR_HEIGHT = 10; - constexpr int CPU_MAX_PER_ROW = 8; - constexpr int CPU_ROWS = 2; + constexpr int CPU_BAR_WIDTH = 10; // 10 + constexpr int CPU_BAR_GAP = 4; // 4 + constexpr int CPU_BAR_HEIGHT = 16; // 16 + constexpr int CPU_MAX_PER_ROW = 8; // 8 + constexpr int CPU_ROWS = 2; // 2 // ===== Memory bar ===== - constexpr int MEM_BAR_HEIGHT = 8; - - // ===== Colors ===== - const Display::Graphics::Color BG_COLOR{20, 20, 20}; - const Display::Graphics::Color BORDER_COLOR{60, 60, 60}; - const Display::Graphics::Color TEXT_COLOR{220, 220, 220}; - const Display::Graphics::Color HEADER_BG{30, 30, 30}; + constexpr int MEM_BAR_HEIGHT = 11; // 8 + constexpr int MEM_BAR_TEXT_PADDING_X = PADDING + 3; class HostBlock { public: HostBlock(); - static constexpr int width() { return 116; } - static constexpr int height() { return 116; } + static constexpr int width() { return BLOCK_WIDTH; } + static constexpr int height() { return BLOCK_HEIGHT; } void draw( Graphics::Framebuffer &fb, diff --git a/src/Display/UI/Text/BitmapFont.cpp b/src/Display/UI/Text/BitmapFont.cpp deleted file mode 100644 index 5ec89ab..0000000 --- a/src/Display/UI/Text/BitmapFont.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Display/UI/Text/BitmapFont.h" - -namespace Display::UI::Text -{ - // 5x7 bitmap digits (0–9) - static const uint8_t digits[10][7] = { - {0x1E, 0x29, 0x25, 0x23, 0x21, 0x29, 0x1E}, // 0 - {0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E}, // 1 - {0x1E, 0x21, 0x01, 0x0E, 0x10, 0x20, 0x3F}, // 2 - {0x1E, 0x21, 0x02, 0x06, 0x01, 0x21, 0x1E}, // 3 - {0x02, 0x06, 0x0A, 0x12, 0x3F, 0x02, 0x02}, // 4 - {0x3F, 0x20, 0x3E, 0x01, 0x01, 0x21, 0x1E}, // 5 - {0x0E, 0x10, 0x20, 0x3E, 0x21, 0x21, 0x1E}, // 6 - {0x3F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08}, // 7 - {0x1E, 0x21, 0x21, 0x1E, 0x21, 0x21, 0x1E}, // 8 - {0x1E, 0x21, 0x21, 0x1F, 0x01, 0x02, 0x1C} // 9 - }; - - void BitmapFont::drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, const Display::Graphics::Color &color) - { - if (d < 0 || d > 9) - return; - - for (int row = 0; row < 7; ++row) - { - for (int col = 0; col < 5; ++col) - { - if (digits[d][row] & (1 << (4 - col))) - { - // Рисуем пиксель как 2x2 квадрат для читаемости - fb.fillRect(x + col * 2, y + row * 2, 2, 2, color); - } - } - } - } -} // namespace Display::UI::Text diff --git a/src/Display/UI/Text/BitmapFont.h b/src/Display/UI/Text/BitmapFont.h deleted file mode 100644 index 19dc31d..0000000 --- a/src/Display/UI/Text/BitmapFont.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include -#include "Display/Graphics/Framebuffer.h" -#include "Display/Graphics/Color.h" - -namespace Display::UI::Text -{ - class BitmapFont - { - public: - void drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, const Display::Graphics::Color &color); - }; -} // namespace Display::UI::Text diff --git a/src/Display/UI/Text/CMakeLists.txt b/src/Display/UI/Text/CMakeLists.txt index 9f7c49b..ad70a27 100644 --- a/src/Display/UI/Text/CMakeLists.txt +++ b/src/Display/UI/Text/CMakeLists.txt @@ -1,9 +1,8 @@ message(STATUS "···Configuring Text") add_library(DisplayUIText - BitmapFont.cpp - FontFace.cpp - GlyphCache.cpp + Fonts.cpp + Helpers.cpp Renderer.cpp ) @@ -15,6 +14,7 @@ find_package(Freetype REQUIRED) target_link_libraries(DisplayUIText PRIVATE Display::Graphics + Helpers::All ${FREETYPE_LIBRARIES} ) diff --git a/src/Display/UI/Text/Font.h b/src/Display/UI/Text/Font.h new file mode 100644 index 0000000..b03cce9 --- /dev/null +++ b/src/Display/UI/Text/Font.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace Display::UI::Text +{ + struct Font + { + std::string name; + int size; // размер шрифта + bool operator==(const Font &other) const + { + return name == other.name && size == other.size; + } + }; + + struct FontHash + { + std::size_t operator()(const Font &k) const + { + return std::hash()(k.name) ^ std::hash()(k.size); + } + }; +} // namespace Display::UI::Text diff --git a/src/Display/UI/Text/FontFace.cpp b/src/Display/UI/Text/FontFace.cpp deleted file mode 100644 index 5173c73..0000000 --- a/src/Display/UI/Text/FontFace.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "Display/UI/Text/FontFace.h" -#include -#include FT_FREETYPE_H -#include - -namespace Display::UI::Text -{ - struct FontFace::FreeType - { - FT_Library library = nullptr; - FT_Face face = nullptr; - }; - - FontFace::FontFace(const std::string &path, int pixelSize) - : freetype(std::make_unique()) - { - if (FT_Init_FreeType(&freetype->library)) - throw std::runtime_error("FT_Init_FreeType failed"); - - if (FT_New_Face(freetype->library, path.c_str(), 0, &freetype->face)) - throw std::runtime_error("FT_New_Face failed"); - - FT_Set_Pixel_Sizes(freetype->face, 0, pixelSize); - } - - FontFace::~FontFace() - { - if (freetype->face) - FT_Done_Face(freetype->face); - if (freetype->library) - FT_Done_FreeType(freetype->library); - } - - void *FontFace::getFace() const - { - return freetype->face; - } -} // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/FontFace.h b/src/Display/UI/Text/FontFace.h deleted file mode 100644 index 3e38f8d..0000000 --- a/src/Display/UI/Text/FontFace.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include - -namespace Display::UI::Text -{ - class FontFace - { - public: - FontFace(const std::string &path, int pixelSize); - ~FontFace(); - - FontFace(const FontFace &) = delete; - FontFace &operator=(const FontFace &) = delete; - - FontFace(FontFace &&) = delete; - FontFace &operator=(FontFace &&) = delete; - - void *getFace() const; - - private: - struct FreeType; - std::unique_ptr freetype; - }; -} // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/Fonts.cpp b/src/Display/UI/Text/Fonts.cpp new file mode 100644 index 0000000..ec010f1 --- /dev/null +++ b/src/Display/UI/Text/Fonts.cpp @@ -0,0 +1,119 @@ +#include "Display/UI/Text/Fonts.h" +#include +#include FT_FREETYPE_H +#include +#include +#include +#include "Helpers/Paths.h" + +namespace Display::UI::Text +{ + struct Fonts::Library + { + FT_Library library = nullptr; + }; + struct Fonts::Face + { + FT_Face face = nullptr; + }; + + Fonts::Fonts() + : library(std::make_unique()) + { + if (FT_Init_FreeType(&library->library)) + throw std::runtime_error("FT_Init_FreeType failed"); + + loadAllFonts(); + } + + Fonts::~Fonts() + { + for (auto &[k, v] : fonts) + if (v->face) + FT_Done_Face(v->face); + if (library->library) + FT_Done_FreeType(library->library); + } + + void Fonts::loadAllFonts() + { + loadFonts("/usr/share/fonts/truetype"); + loadFonts(Helpers::initPaths().exeDir); + } + + void Fonts::loadFonts(const std::string &path) + { + if (std::filesystem::exists(path)) + { + for (auto &p : std::filesystem::recursive_directory_iterator(path)) + { + if (p.path().extension() == ".ttf" || p.path().extension() == ".otf") + { + std::string path = p.path().string(); + std::string filename = p.path().stem().string(); + + // Можно сразу создавать Fonts для стандартных размеров, например: 10,12,14,16,18,24 + for (int sz : {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 24, 36, 72}) + { + Font key{filename, sz}; + + std::unique_ptr face = std::make_unique(); + if (FT_New_Face(library->library, path.c_str(), 0, &face->face)) + throw std::runtime_error("FT_New_Face failed"); + FT_Set_Pixel_Sizes(face->face, 0, sz); + + fonts.emplace(key, std::move(face)); + } + } + } + } + } + + const Glyph &Fonts::getGlyph(char ch, const Font &font) + { + + GlyphKey key{ch, font}; + + auto gph = glyphs.find(key); + if (gph != glyphs.end()) + return gph->second; + + auto fnt = fonts.find(font); + if (fnt == fonts.end()) + { + // Если нет, можно использовать какой-нибудь дефолтный шрифт + fnt = fonts.begin(); + } + + FT_Face face = fnt->second->face; + + if (!face) + throw std::runtime_error("Fonts not initialized!"); + if (FT_Load_Char(face, ch, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO)) + throw std::runtime_error("FT_Load_Char failed"); + FT_GlyphSlot g = face->glyph; + + Glyph glyph; + glyph.width = g->bitmap.width; + glyph.height = g->bitmap.rows; + glyph.bearingX = g->bitmap_left; + glyph.bearingY = g->bitmap_top; + glyph.advance = g->advance.x >> 6; + // glyph.buffer.assign(g->bitmap.buffer, g->bitmap.buffer + g->bitmap.width * g->bitmap.rows); + + glyph.buffer.resize(glyph.width * glyph.height, 0); + + const FT_Bitmap &bm = g->bitmap; + for (int y = 0; y < bm.rows; ++y) + { + for (int x = 0; x < bm.width; ++x) + { + int byteIndex = y * bm.pitch + x / 8; + int bitIndex = 7 - (x % 8); + uint8_t bit = (bm.buffer[byteIndex] >> bitIndex) & 1; + glyph.buffer[y * bm.width + x] = bit ? 255 : 0; + } + } + return glyphs.emplace(key, std::move(glyph)).first->second; + } +} // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/Fonts.h b/src/Display/UI/Text/Fonts.h new file mode 100644 index 0000000..3a3d0ef --- /dev/null +++ b/src/Display/UI/Text/Fonts.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include +#include "Display/UI/Text/Font.h" +#include "Display/UI/Text/Glyph.h" +#include "Display/UI/Text/GlyphKey.h" + +namespace Display::UI::Text +{ + class Fonts + { + public: + Fonts(); + ~Fonts(); + + const Glyph &getGlyph(char ch, const Font &key); + + private: + struct Library; + struct Face; + std::unique_ptr library; + std::unordered_map, FontHash> fonts; + std::unordered_map glyphs; + void loadAllFonts(); + void loadFonts(const std::string &path); + }; +} // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/GlyphCache.cpp b/src/Display/UI/Text/GlyphCache.cpp deleted file mode 100644 index 6b18ccf..0000000 --- a/src/Display/UI/Text/GlyphCache.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "Display/UI/Text/GlyphCache.h" -#include -#include -#include "Display/UI/Text/FontFace.h" -#include -#include FT_FREETYPE_H - -namespace Display::UI::Text -{ - GlyphCache::GlyphCache(const FontFace &fontFace) : fontFace(fontFace) {} - - const Glyph &GlyphCache::getGlyph(char c) - { - FT_Face face = static_cast(fontFace.getFace()); - auto it = cache.find(c); - if (it != cache.end()) - return it->second; - - if (!face) - throw std::runtime_error("FontFace not initialized!"); - if (FT_Load_Char(face, c, FT_LOAD_RENDER)) - throw std::runtime_error("FT_Load_Char failed"); - FT_GlyphSlot g = face->glyph; - - Glyph glyph; - glyph.width = g->bitmap.width; - glyph.height = g->bitmap.rows; - glyph.bearingX = g->bitmap_left; - glyph.bearingY = g->bitmap_top; - glyph.advance = g->advance.x >> 6; - glyph.buffer.assign(g->bitmap.buffer, g->bitmap.buffer + g->bitmap.width * g->bitmap.rows); - - return cache.emplace(c, std::move(glyph)).first->second; - } -} // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/GlyphCache.h b/src/Display/UI/Text/GlyphCache.h deleted file mode 100644 index 799f497..0000000 --- a/src/Display/UI/Text/GlyphCache.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "Display/UI/Text/Glyph.h" - -#include -#include - -namespace Display::UI::Text -{ - class FontFace; - class GlyphCache - { - public: - GlyphCache(const FontFace &fontFace); - - GlyphCache(const GlyphCache &) = delete; - GlyphCache &operator=(const GlyphCache &) = delete; - - GlyphCache(GlyphCache &&) = delete; - GlyphCache &operator=(GlyphCache &&) = delete; - - const Glyph &getGlyph(char c); - - private: - const FontFace &fontFace; - std::unordered_map cache; - }; -} // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/GlyphKey.h b/src/Display/UI/Text/GlyphKey.h new file mode 100644 index 0000000..d1cd4e1 --- /dev/null +++ b/src/Display/UI/Text/GlyphKey.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "Font.h" + +namespace Display::UI::Text +{ + struct GlyphKey + { + char ch; + Font font; + + bool operator==(const GlyphKey &other) const + { + return ch == other.ch && font == other.font; + } + }; + + struct GlyphKeyHash + { + std::size_t operator()(const GlyphKey &k) const + { + std::size_t h1 = std::hash()(k.ch); + std::size_t h2 = FontHash{}(k.font); + return h1 ^ (h2 << 1); + } + }; +} // namespace Display::UI::Text diff --git a/src/Display/UI/Text/Helpers.cpp b/src/Display/UI/Text/Helpers.cpp new file mode 100644 index 0000000..886bf94 --- /dev/null +++ b/src/Display/UI/Text/Helpers.cpp @@ -0,0 +1,20 @@ +#include "Display/UI/Text/Helpers.h" + +#include + +namespace Display::UI::Text +{ + std::string formatFloat(float value, int decimals) + { + char buffer[64]; + std::snprintf(buffer, sizeof(buffer), "%.*f", decimals, value); + return std::string(buffer); + } + + std::string formatDouble(double value, int decimals) + { + char buffer[64]; + std::snprintf(buffer, sizeof(buffer), "%.*f", decimals, value); + return std::string(buffer); + } +} // namespace Display::UI::Text diff --git a/src/Display/UI/Text/Helpers.h b/src/Display/UI/Text/Helpers.h new file mode 100644 index 0000000..6f4c31c --- /dev/null +++ b/src/Display/UI/Text/Helpers.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace Display::UI::Text +{ + std::string formatFloat(float value, int decimals = 1); + std::string formatDouble(double value, int decimals = 1); +} // namespace Display::UI::Text diff --git a/src/Display/UI/Text/Renderer.cpp b/src/Display/UI/Text/Renderer.cpp index 1e3ed49..e3d8f73 100644 --- a/src/Display/UI/Text/Renderer.cpp +++ b/src/Display/UI/Text/Renderer.cpp @@ -5,18 +5,15 @@ namespace Display::UI::Text { - - Renderer::Renderer() : fontFace("/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf", 12), glyphCache(fontFace) {} - void Renderer::drawText(Display::Graphics::Framebuffer &fb, int x, int y, - const std::string &text, const Display::Graphics::Color &color) + const std::string &text, const Display::Graphics::Color &color, const Font &font) { int penX = x; int baseline = y; for (char ch : text) { - const Glyph &g = glyphCache.getGlyph(ch); + const Glyph &g = fonts.getGlyph(ch, font); for (int row = 0; row < g.height; ++row) { @@ -44,4 +41,46 @@ namespace Display::UI::Text } } + void Renderer::drawTextOutlined(Display::Graphics::Framebuffer &fb, + int x, int y, + const std::string &text, + const Display::Graphics::Color &colorText, + const Display::Graphics::Color &colorOutline, + const Font &font) + { + // outline + for (int dx = -1; dx <= 1; ++dx) + for (int dy = -1; dy <= 1; ++dy) + if (dx != 0 || dy != 0) + drawText(fb, x + dx, y + dy, text, colorOutline, font); + + // main text + drawText(fb, x, y, text, colorText, font); + } + + /* int Renderer::measureWidth(const std::string &text) + { + int width = 0; + for (char ch : text) + { + const Glyph &g = glyphCache.getGlyph(ch, face); + width += g.advance; + } + return width; + } + + int Renderer::measureHeight(const std::string &text) + { + int height = 0; + for (char ch : text) + { + const Glyph &g = glyphCache.getGlyph(ch, face); + int glyphHeight = g.height - g.bearingY + g.bearingY; // можно уточнить по высоте глифа + if (glyphHeight > height) + height = glyphHeight; + } + return height; + } + */ + } // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Text/Renderer.h b/src/Display/UI/Text/Renderer.h index dcb59cb..e4e8b73 100644 --- a/src/Display/UI/Text/Renderer.h +++ b/src/Display/UI/Text/Renderer.h @@ -1,11 +1,12 @@ #pragma once #include "Display/Graphics/Framebuffer.h" -#include "Display/UI/Text/FontFace.h" -#include "Display/UI/Text/GlyphCache.h" +#include "Display/UI/Text/Fonts.h" +#include "Display/UI/Text/Font.h" #include "Display/Graphics/Color.h" #include +#include namespace Display::UI::Text { @@ -13,16 +14,14 @@ namespace Display::UI::Text class Renderer { public: - Renderer(); + void drawText(Display::Graphics::Framebuffer &fb, int x, int y, const std::string &text, const Display::Graphics::Color &color = Display::Graphics::Color{255, 255, 255}, const Font &font = {"LiberationSans-Regular", 12}); + void drawTextOutlined(Display::Graphics::Framebuffer &fb, int x, int y, const std::string &text, const Display::Graphics::Color &colorText = Display::Graphics::Color{255, 255, 255}, const Display::Graphics::Color &colorOutline = Display::Graphics::Color{0, 0, 0}, const Font &font = {"LiberationSans-Regular", 12}); - Renderer(const Renderer &) = delete; - Renderer &operator=(const Renderer &) = delete; - - void drawText(Display::Graphics::Framebuffer &fb, int x, int y, const std::string &text, const Display::Graphics::Color &color); + // int measureWidth(const std::string &text); + // int measureHeight(const std::string &text); private: - FontFace fontFace; - GlyphCache glyphCache; + Fonts fonts; }; } // namespace Display::UI::Text \ No newline at end of file diff --git a/src/Display/UI/Theme/CMakeLists.txt b/src/Display/UI/Theme/CMakeLists.txt new file mode 100644 index 0000000..6ba7839 --- /dev/null +++ b/src/Display/UI/Theme/CMakeLists.txt @@ -0,0 +1,15 @@ +message(STATUS "···Configuring Theme") + +add_library(Theme INTERFACE) + +add_library(Display::UI::Theme ALIAS Theme) + +target_link_libraries(Theme + INTERFACE + Display::Graphics +) + +target_include_directories(Theme + INTERFACE + ${INCLUDE_BASE_DIR} +) diff --git a/src/Display/UI/Theme/Theme.h b/src/Display/UI/Theme/Theme.h new file mode 100644 index 0000000..f2f753f --- /dev/null +++ b/src/Display/UI/Theme/Theme.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Display/Graphics/Color.h" + +namespace Display::UI::Theme +{ + namespace Text + { + const Display::Graphics::Color TEXT{220, 220, 220}; + const Display::Graphics::Color OUTLINE{20, 20, 20}; + } // namespace Text + + namespace Header + { + // ===== Header ===== + const Display::Graphics::Color BACKGROUND{20, 20, 20}; + const Display::Graphics::Color BORDER{60, 60, 60}; + const Display::Graphics::Color HEADER{30, 30, 30}; + } // namespace Header + + namespace HostBlock + { + // ===== HostBlock ===== + const Display::Graphics::Color BACKGROUND{20, 20, 20}; + const Display::Graphics::Color BORDER{60, 60, 60}; + const Display::Graphics::Color HEADER{30, 30, 30}; + } // namespace HostBlock + + namespace Bar + { + const Display::Graphics::Color BACKGROUND{30, 30, 30}; + const Display::Graphics::Color FILL{0, 180, 0}; + const Display::Graphics::Color BORDER{80, 80, 80}; + } // namespace Bar +} // namespace Display::UI::Theme diff --git a/src/Helpers/CMakeLists.txt b/src/Helpers/CMakeLists.txt index 73b4cd9..4c46ecc 100644 --- a/src/Helpers/CMakeLists.txt +++ b/src/Helpers/CMakeLists.txt @@ -5,12 +5,12 @@ configure_file( ${CMAKE_CURRENT_BINARY_DIR}/Version.h ) -add_library(Helpers INTERFACE) +add_library(Helpers Paths.cpp) add_library(Helpers::All ALIAS Helpers) target_include_directories(Helpers - INTERFACE + PUBLIC ${INCLUDE_BASE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/src/Helpers/Paths.cpp b/src/Helpers/Paths.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Helpers/Paths.h b/src/Helpers/Paths.h new file mode 100644 index 0000000..f792013 --- /dev/null +++ b/src/Helpers/Paths.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +namespace Helpers +{ + std::filesystem::path getExecutablePath() + { + char buf[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (len == -1) + throw std::runtime_error("readlink(/proc/self/exe) failed"); + + buf[len] = '\0'; + return std::filesystem::path(buf); + } + + struct Paths + { + std::filesystem::path exe; + std::filesystem::path exeDir; + std::filesystem::path configDir; + std::filesystem::path assetsDir; + }; + + Paths initPaths() + { + Paths p; + p.exe = getExecutablePath(); + p.exeDir = p.exe.parent_path(); + p.configDir = p.exeDir / "config"; + p.assetsDir = p.exeDir / "assets"; + return p; + } +} // namespace Helpers diff --git a/src/Metrics/Collector.cpp b/src/Metrics/Collector.cpp index 717385b..6af07e9 100644 --- a/src/Metrics/Collector.cpp +++ b/src/Metrics/Collector.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include namespace Metrics { @@ -99,36 +101,49 @@ namespace Metrics uint64_t value; std::string unit; - uint64_t total = 0, available = 0; + uint64_t mem_total = 0, mem_available = 0, swap_total = 0, swap_available = 0; while (f >> key >> value >> unit) { if (key == "MemTotal:") - total = value * 1024; + mem_total = value * 1024; else if (key == "MemAvailable:") - available = value * 1024; + mem_available = value * 1024; + else if (key == "SwapTotal:") + swap_total = value * 1024; + else if (key == "SwapFree:") + swap_available = value * 1024; } Memory memory; - memory.total = total; - memory.available = available; - memory.used = total - available; + memory.mem_total = mem_total; + memory.mem_available = mem_available; + memory.mem_used = mem_total - mem_available; + memory.swap_total = swap_total; + memory.swap_available = swap_available; + memory.swap_used = swap_total - swap_available; return memory; } Disk Collector::readDisk(const char *path) { - struct statvfs vfs{}; - statvfs(path, &vfs); + struct statvfs vfs; + if (statvfs(path, &vfs) != 0) + { + throw std::runtime_error( + std::string("statvfs failed for ") + path + ": " + + std::strerror(errno)); + } - uint64_t total = vfs.f_blocks * vfs.f_frsize; - uint64_t free = vfs.f_bavail * vfs.f_frsize; + uint64_t total = static_cast(vfs.f_blocks) * vfs.f_frsize; + uint64_t free = static_cast(vfs.f_bavail) * vfs.f_frsize; + uint64_t used = total - free; Disk d; d.name = path; d.total = total; d.free = free; - d.used = total - free; + d.used = used; return d; } diff --git a/src/Metrics/Host.cpp b/src/Metrics/Host.cpp index b87f11d..c1133b0 100644 --- a/src/Metrics/Host.cpp +++ b/src/Metrics/Host.cpp @@ -41,8 +41,10 @@ namespace Metrics buf.writeFloat(f); // Memory - buf.writeFloat(memory.used); - buf.writeFloat(memory.total); + buf.writeFloat(memory.mem_used); + buf.writeFloat(memory.mem_total); + buf.writeFloat(memory.swap_used); + buf.writeFloat(memory.swap_total); // Disks buf.writeUint8(static_cast(disks.size())); @@ -77,8 +79,10 @@ namespace Metrics h.cpu.coreLoads.push_back(buf.readFloat()); // Memory - h.memory.used = buf.readFloat(); - h.memory.total = buf.readFloat(); + h.memory.mem_used = buf.readFloat(); + h.memory.mem_total = buf.readFloat(); + h.memory.swap_used = buf.readFloat(); + h.memory.swap_total = buf.readFloat(); // Disks uint8_t numDisks = buf.readUint8(); diff --git a/src/Metrics/Memory.h b/src/Metrics/Memory.h index b4932e9..bd9dfc0 100644 --- a/src/Metrics/Memory.h +++ b/src/Metrics/Memory.h @@ -4,8 +4,11 @@ namespace Metrics { struct Memory { - float used; - float available; - float total; + float mem_used; + float mem_available; + float mem_total; + float swap_used; + float swap_total; + float swap_available; }; } // namespace Metrics diff --git a/src/Model/HostRegistry.cpp b/src/Model/HostRegistry.cpp index e6e89b1..c145f5e 100644 --- a/src/Model/HostRegistry.cpp +++ b/src/Model/HostRegistry.cpp @@ -4,13 +4,13 @@ namespace Model { void HostRegistry::update(const std::string &host, const Metrics::Host &m) { - std::lock_guard lock(mutex_); - hosts_[host] = m; + std::lock_guard lock(mutex); + hosts[host] = m; } std::map HostRegistry::snapshot() { - std::lock_guard lock(mutex_); - return hosts_; + std::lock_guard lock(mutex); + return hosts; } } // namespace Model diff --git a/src/Model/HostRegistry.h b/src/Model/HostRegistry.h index b1b651d..54eba7f 100644 --- a/src/Model/HostRegistry.h +++ b/src/Model/HostRegistry.h @@ -13,7 +13,7 @@ namespace Model std::map snapshot(); private: - std::map hosts_; - std::mutex mutex_; + std::map hosts; + std::mutex mutex; }; } // namespace Model