Add correct fonts

This commit is contained in:
2025-12-28 22:28:26 +00:00
parent a818813877
commit 85295ae4d8
35 changed files with 626 additions and 257 deletions
+1
View File
@@ -13,6 +13,7 @@ target_link_libraries(DisplayGraphics
Helpers::All
Display::UI::Text
Display::UI::HostBlock
Display::UI::Header
)
target_include_directories(DisplayGraphics
+3 -1
View File
@@ -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,
+5 -3
View File
@@ -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 <string>
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 &registry;
Display::UI::Text::Renderer textRenderer;
Display::UI::Header::Header header;
Display::UI::HostBlock::HostBlock hostblock;
};
} // namespace Display::Graphics
+4 -3
View File
@@ -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),
+2
View File
@@ -2,4 +2,6 @@ message(STATUS "··Configuring UI")
add_subdirectory(Bar)
add_subdirectory(Text)
add_subdirectory(Header)
add_subdirectory(HostBlock)
add_subdirectory(Theme)
+18
View File
@@ -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}
)
+58
View File
@@ -0,0 +1,58 @@
#include "Display/UI/Header/Header.h"
#include <algorithm>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
#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();
}
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <vector>
#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();
};
}
+64 -11
View File
@@ -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});
}
}
}
+16 -20
View File
@@ -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,
-36
View File
@@ -1,36 +0,0 @@
#include "Display/UI/Text/BitmapFont.h"
namespace Display::UI::Text
{
// 5x7 bitmap digits (09)
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
-13
View File
@@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
#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
+3 -3
View File
@@ -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}
)
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <string>
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<std::string>()(k.name) ^ std::hash<int>()(k.size);
}
};
} // namespace Display::UI::Text
-38
View File
@@ -1,38 +0,0 @@
#include "Display/UI/Text/FontFace.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdexcept>
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<FreeType>())
{
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
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <memory>
#include <string>
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> freetype;
};
} // namespace Display::UI::Text
+119
View File
@@ -0,0 +1,119 @@
#include "Display/UI/Text/Fonts.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdexcept>
#include <iostream>
#include <filesystem>
#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<Library>())
{
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> face = std::make_unique<Face>();
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
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#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> library;
std::unordered_map<Font, std::unique_ptr<Face>, FontHash> fonts;
std::unordered_map<GlyphKey, Glyph, GlyphKeyHash> glyphs;
void loadAllFonts();
void loadFonts(const std::string &path);
};
} // namespace Display::UI::Text
-35
View File
@@ -1,35 +0,0 @@
#include "Display/UI/Text/GlyphCache.h"
#include <stdexcept>
#include <iostream>
#include "Display/UI/Text/FontFace.h"
#include <ft2build.h>
#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<FT_Face>(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
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include "Display/UI/Text/Glyph.h"
#include <unordered_map>
#include <cstdint>
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<char, Glyph> cache;
};
} // namespace Display::UI::Text
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <string>
#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<char>()(k.ch);
std::size_t h2 = FontHash{}(k.font);
return h1 ^ (h2 << 1);
}
};
} // namespace Display::UI::Text
+20
View File
@@ -0,0 +1,20 @@
#include "Display/UI/Text/Helpers.h"
#include <cstdio>
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
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <string>
namespace Display::UI::Text
{
std::string formatFloat(float value, int decimals = 1);
std::string formatDouble(double value, int decimals = 1);
} // namespace Display::UI::Text
+44 -5
View File
@@ -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
+8 -9
View File
@@ -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 <string>
#include <unordered_map>
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
+15
View File
@@ -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}
)
+35
View File
@@ -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
+2 -2
View File
@@ -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}
)
View File
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <filesystem>
#include <unistd.h>
#include <limits.h>
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
+26 -11
View File
@@ -5,6 +5,8 @@
#include <sys/statvfs.h>
#include <unistd.h>
#include <limits.h>
#include <cerrno>
#include <cstring>
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<uint64_t>(vfs.f_blocks) * vfs.f_frsize;
uint64_t free = static_cast<uint64_t>(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;
}
+8 -4
View File
@@ -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<uint8_t>(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();
+6 -3
View File
@@ -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
+4 -4
View File
@@ -4,13 +4,13 @@ namespace Model
{
void HostRegistry::update(const std::string &host, const Metrics::Host &m)
{
std::lock_guard<std::mutex> lock(mutex_);
hosts_[host] = m;
std::lock_guard<std::mutex> lock(mutex);
hosts[host] = m;
}
std::map<std::string, Metrics::Host> HostRegistry::snapshot()
{
std::lock_guard<std::mutex> lock(mutex_);
return hosts_;
std::lock_guard<std::mutex> lock(mutex);
return hosts;
}
} // namespace Model
+2 -2
View File
@@ -13,7 +13,7 @@ namespace Model
std::map<std::string, Metrics::Host> snapshot();
private:
std::map<std::string, Metrics::Host> hosts_;
std::mutex mutex_;
std::map<std::string, Metrics::Host> hosts;
std::mutex mutex;
};
} // namespace Model