wip(refactor): Improve parsing and font glyph caching
This commit is contained in:
parent
9f9f438fae
commit
b2e8428550
@ -1,5 +1,5 @@
|
||||
---
|
||||
Checks: '-*,performance-*,readability-*,modernize-use-*,modernize-*,-modernize-raw-string-literal,-modernize-use-bool-literals,-readability-implicit-bool-cast,-readability-else-after-return,-readability-named-parameter'
|
||||
Checks: '-*,performance-*,readability-*,clang-analyzer-alpha.core*,clang-analyzer-alpha.security*,clang-analyzer-alpha.unix.cstring*,clang-analyzer-core.uninitialized*,clang-analyzer-cplusplus.*,clang-analyzer-nullability*,clang-analyzer-unix*,cppcoreguidelines*,modernize-use-*,modernize-*,-modernize-raw-string-literal,-modernize-use-bool-literals,-readability-implicit-bool-cast,-readability-else-after-return,-readability-named-parameter,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-cstyle-cast,-cppcoreguidelines-pro-bounds-constant-array-index'
|
||||
CheckOptions:
|
||||
- key: modernize-loop-convert.NamingStyle
|
||||
value: lower_case
|
||||
|
@ -27,10 +27,6 @@
|
||||
#define STDERR_FILENO 2
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "debug.hpp"
|
||||
#endif
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
namespace placeholders = std::placeholders;
|
||||
|
@ -17,6 +17,11 @@ DEFINE_CHILD_ERROR(unclosed_actionblocks, parser_error);
|
||||
|
||||
class parser {
|
||||
public:
|
||||
struct packet {
|
||||
uint16_t data[128]{0U};
|
||||
size_t length{0};
|
||||
};
|
||||
|
||||
explicit parser(signal_emitter& emitter, const bar_settings& bar);
|
||||
void operator()(string data);
|
||||
|
||||
|
@ -32,6 +32,9 @@ class renderer
|
||||
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts);
|
||||
~renderer();
|
||||
|
||||
renderer(const renderer& o) = delete;
|
||||
renderer& operator=(const renderer& o) = delete;
|
||||
|
||||
xcb_window_t window() const;
|
||||
|
||||
void begin();
|
||||
@ -55,8 +58,7 @@ class renderer
|
||||
void fill_underline(int16_t x, uint16_t w);
|
||||
void fill_shift(const int16_t px);
|
||||
|
||||
void draw_character(const uint16_t character);
|
||||
void draw_textstring(const char* text, const size_t len);
|
||||
void draw_textstring(const uint16_t* text, size_t len);
|
||||
|
||||
void begin_action(const mousebtn btn, const string& cmd);
|
||||
void end_action(const mousebtn btn);
|
||||
|
@ -1,11 +1,26 @@
|
||||
#ifdef DEBUG
|
||||
#pragma once
|
||||
|
||||
#ifndef DEBUG
|
||||
#error "Not a debug build..."
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
namespace debug_util {
|
||||
template <class T>
|
||||
void benchmark_execution_speed(const T& expr) noexcept {
|
||||
void loop(const T& expr, size_t iterations) noexcept {
|
||||
while (iterations--) {
|
||||
expr();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void execution_speed(const T& expr) noexcept {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
expr();
|
||||
auto finish = std::chrono::high_resolution_clock::now();
|
||||
@ -14,8 +29,14 @@ void benchmark_execution_speed(const T& expr) noexcept {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void benchmark_memory_usage(const T& object) noexcept {
|
||||
std::cout << "memory usage: " << sizeof(object) << "b" << std::endl;
|
||||
void execution_speed(const T& expr, size_t iterations) noexcept {
|
||||
execution_speed([=] { loop(expr, iterations); });
|
||||
}
|
||||
|
||||
#endif
|
||||
template <class T>
|
||||
void memory_usage(const T& object) noexcept {
|
||||
std::cout << "memory usage: " << sizeof(object) << "b" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
POLYBAR_NS_END
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "components/eventloop.hpp"
|
||||
#include "components/ipc.hpp"
|
||||
#include "components/parser.hpp"
|
||||
#include "components/types.hpp"
|
||||
#include "utils/functional.hpp"
|
||||
|
||||
@ -114,6 +115,8 @@ namespace signals {
|
||||
}
|
||||
|
||||
namespace parser {
|
||||
using parser_t = polybar::parser;
|
||||
|
||||
DEFINE_VALUE_SIGNAL(70, change_background, uint32_t);
|
||||
DEFINE_VALUE_SIGNAL(71, change_foreground, uint32_t);
|
||||
DEFINE_VALUE_SIGNAL(72, change_underline, uint32_t);
|
||||
@ -128,7 +131,7 @@ namespace signals {
|
||||
DEFINE_VALUE_SIGNAL(81, action_end, mousebtn);
|
||||
DEFINE_VALUE_SIGNAL(82, write_text_ascii, uint16_t);
|
||||
DEFINE_VALUE_SIGNAL(83, write_text_unicode, uint16_t);
|
||||
DEFINE_VALUE_SIGNAL(84, write_text_string, string);
|
||||
DEFINE_VALUE_SIGNAL(84, write_text_string, parser_t::packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_OUTLINE_H
|
||||
#include FT_GLYPH_H
|
||||
|
||||
#include <X11/Xft/Xft.h>
|
||||
#include <xcb/xcbext.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "x11/color.hpp"
|
||||
@ -9,12 +15,17 @@
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
using std::map;
|
||||
using std::unordered_map;
|
||||
|
||||
// fwd
|
||||
class connection;
|
||||
class logger;
|
||||
|
||||
struct fonttype {
|
||||
explicit fonttype() = default;
|
||||
struct font_ref {
|
||||
explicit font_ref() = default;
|
||||
font_ref(const font_ref& o) = delete;
|
||||
font_ref& operator=(const font_ref& o) = delete;
|
||||
XftFont* xft{nullptr};
|
||||
xcb_font_t ptr{XCB_NONE};
|
||||
int offset_y{0};
|
||||
@ -25,57 +36,61 @@ struct fonttype {
|
||||
uint16_t char_max{0};
|
||||
uint16_t char_min{0};
|
||||
vector<xcb_charinfo_t> width_lut{};
|
||||
};
|
||||
unordered_map<uint16_t, wchar_t> glyph_widths{};
|
||||
|
||||
struct fonttype_deleter {
|
||||
void operator()(fonttype* f);
|
||||
static struct _deleter {
|
||||
void operator()(font_ref* font);
|
||||
} deleter;
|
||||
};
|
||||
|
||||
using fonttype_pointer = unique_ptr<fonttype, fonttype_deleter>;
|
||||
|
||||
class font_manager {
|
||||
public:
|
||||
using make_type = unique_ptr<font_manager>;
|
||||
static make_type make();
|
||||
|
||||
explicit font_manager(connection& conn, const logger& logger, shared_ptr<Display>&& dsp, shared_ptr<Visual>&& vis);
|
||||
explicit font_manager(
|
||||
connection& conn, const logger& logger, shared_ptr<Display>&& dsp, shared_ptr<Visual>&& vis, Colormap&& cm);
|
||||
~font_manager();
|
||||
|
||||
font_manager(const font_manager& o) = delete;
|
||||
font_manager& operator=(const font_manager& o) = delete;
|
||||
|
||||
void cleanup();
|
||||
bool load(const string& name, int8_t fontindex = DEFAULT_FONT_INDEX, int8_t offset_y = 0);
|
||||
|
||||
void set_preferred_font(int8_t index);
|
||||
|
||||
fonttype_pointer& match_char(uint16_t chr);
|
||||
uint8_t char_width(fonttype_pointer& font, uint16_t chr);
|
||||
|
||||
XftColor* xftcolor();
|
||||
XftDraw* xftdraw();
|
||||
|
||||
void create_xftdraw(xcb_pixmap_t pm);
|
||||
void destroy_xftdraw();
|
||||
shared_ptr<font_ref> match_char(const uint16_t chr);
|
||||
uint8_t glyph_width(const shared_ptr<font_ref>& font, const uint16_t chr);
|
||||
void drawtext(const shared_ptr<font_ref>& font, xcb_pixmap_t pm, xcb_gcontext_t gc, int16_t x, int16_t y,
|
||||
const uint16_t* chars, size_t num_chars);
|
||||
|
||||
void allocate_color(uint32_t color);
|
||||
void allocate_color(XRenderColor color);
|
||||
|
||||
void set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font);
|
||||
void set_gcontext_font(const shared_ptr<font_ref>& font, xcb_gcontext_t, xcb_font_t*);
|
||||
|
||||
protected:
|
||||
bool open_xcb_font(fonttype_pointer& fontptr, string fontname);
|
||||
bool has_glyph(fonttype_pointer& font, uint16_t chr);
|
||||
bool open_xcb_font(const shared_ptr<font_ref>& font, string fontname);
|
||||
|
||||
uint8_t glyph_width_xft(const shared_ptr<font_ref>& font, const uint16_t chr);
|
||||
uint8_t glyph_width_xcb(const shared_ptr<font_ref>& font, const uint16_t chr);
|
||||
|
||||
bool has_glyph_xft(const shared_ptr<font_ref>& font, const uint16_t chr);
|
||||
bool has_glyph_xcb(const shared_ptr<font_ref>& font, const uint16_t chr);
|
||||
|
||||
private:
|
||||
connection& m_connection;
|
||||
const logger& m_logger;
|
||||
|
||||
shared_ptr<Display> m_display{nullptr};
|
||||
shared_ptr<Visual> m_visual{nullptr};
|
||||
Colormap m_colormap{};
|
||||
shared_ptr<Display> m_display;
|
||||
shared_ptr<Visual> m_visual;
|
||||
Colormap m_colormap;
|
||||
|
||||
std::map<uint8_t, fonttype_pointer> m_fonts;
|
||||
map<uint8_t, shared_ptr<font_ref>> m_fonts{};
|
||||
int8_t m_fontindex{DEFAULT_FONT_INDEX};
|
||||
|
||||
XftColor* m_xftcolor{nullptr};
|
||||
XftDraw* m_xftdraw{nullptr};
|
||||
XftColor m_xftcolor{};
|
||||
bool m_xftcolor_allocated{false};
|
||||
};
|
||||
|
||||
POLYBAR_NS_END
|
||||
|
@ -140,6 +140,7 @@ make_executable(${PROJECT_NAME} SOURCES
|
||||
target_link_libraries(${PROJECT_NAME} Threads::Threads)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC
|
||||
${X11_Xft_DEFINITIONS}
|
||||
${X11_XCB_DEFINITIONS}
|
||||
${XCB_DEFINITIONS})
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "events/signal.hpp"
|
||||
#include "events/signal_emitter.hpp"
|
||||
#include "utils/math.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
@ -29,8 +30,7 @@ void parser::operator()(string data) {
|
||||
} else if ((pos = data.find("%{")) != string::npos) {
|
||||
data.erase(0, text(data.substr(0, pos)));
|
||||
} else {
|
||||
text(move(data));
|
||||
return;
|
||||
data.erase(0, text(data.substr(0)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,20 +155,32 @@ void parser::codeblock(string&& data) {
|
||||
* Process text contents
|
||||
*/
|
||||
size_t parser::text(string&& data) {
|
||||
uint8_t* utf = reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str()));
|
||||
const uint8_t* utf{reinterpret_cast<const uint8_t*>(&data[0])};
|
||||
|
||||
if (utf[0] < 0x80) {
|
||||
// grab all consecutive ascii chars
|
||||
size_t next_tag = data.find("%{");
|
||||
if (next_tag != string::npos) {
|
||||
data.erase(next_tag);
|
||||
size_t pos{0};
|
||||
|
||||
// grab consecutive ascii chars
|
||||
while (utf[pos] && utf[pos] < 0x80) {
|
||||
packet pkt{};
|
||||
size_t limit{memory_util::countof(pkt.data)};
|
||||
size_t len{0};
|
||||
|
||||
while (len + 1 < limit && utf[pos] && utf[pos] < 0x80) {
|
||||
pkt.data[len++] = utf[pos++];
|
||||
}
|
||||
size_t n = 0;
|
||||
while (utf[n] != '\0' && utf[++n] < 0x80) {
|
||||
;
|
||||
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
pkt.length = len;
|
||||
m_sig.emit(write_text_string{move(pkt)});
|
||||
}
|
||||
|
||||
if (pos > 0) {
|
||||
return pos;
|
||||
}
|
||||
m_sig.emit(write_text_string{data.substr(0, n)});
|
||||
return n;
|
||||
} else if ((utf[0] & 0xe0) == 0xc0) { // 2 byte utf-8 sequence
|
||||
m_sig.emit(write_text_unicode{static_cast<uint16_t>((utf[0] & 0x1f) << 6 | (utf[1] & 0x3f))});
|
||||
return 2;
|
||||
@ -185,10 +197,13 @@ size_t parser::text(string&& data) {
|
||||
} else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence
|
||||
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
|
||||
return 6;
|
||||
} else { // invalid utf-8 sequence
|
||||
m_sig.emit(write_text_ascii{utf[0]});
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (utf[0] < 0x80) {
|
||||
m_sig.emit(write_text_ascii{utf[0]});
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,15 +47,18 @@ renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logg
|
||||
|
||||
if ((m_visual = m_connection.visual_type(m_connection.screen(), 32)) == nullptr) {
|
||||
m_log.err("No 32-bit TrueColor visual found...");
|
||||
m_depth = 24;
|
||||
}
|
||||
|
||||
if ((m_visual = m_connection.visual_type(m_connection.screen(), 24)) == nullptr) {
|
||||
m_log.err("No 24-bit TrueColor visual found, aborting...");
|
||||
}
|
||||
|
||||
if (m_visual == nullptr) {
|
||||
throw application_error("No matching TrueColor visual found...");
|
||||
}
|
||||
|
||||
m_depth = 24;
|
||||
}
|
||||
|
||||
m_log.trace("renderer: Allocate colormap");
|
||||
m_colormap = m_connection.generate_id();
|
||||
m_connection.create_colormap(XCB_COLORMAP_ALLOC_NONE, m_colormap, m_connection.screen()->root, m_visual->visual_id);
|
||||
@ -66,7 +69,7 @@ renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logg
|
||||
m_window = winspec(m_connection)
|
||||
<< cw_size(m_bar.size)
|
||||
<< cw_pos(m_bar.pos)
|
||||
<< cw_depth(32)
|
||||
<< cw_depth(m_depth)
|
||||
<< cw_visual(m_visual->visual_id)
|
||||
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
|
||||
<< cw_params_back_pixel(0)
|
||||
@ -83,7 +86,7 @@ renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logg
|
||||
|
||||
m_log.trace("renderer: Allocate window pixmap");
|
||||
m_pixmap = m_connection.generate_id();
|
||||
m_connection.create_pixmap(32, m_pixmap, m_window, m_rect.width, m_rect.height);
|
||||
m_connection.create_pixmap(m_depth, m_pixmap, m_window, m_rect.width, m_rect.height);
|
||||
|
||||
m_log.trace("renderer: Allocate graphic contexts");
|
||||
{
|
||||
@ -180,8 +183,6 @@ void renderer::begin() {
|
||||
m_currentx = 0;
|
||||
m_attributes = 0;
|
||||
m_actions.clear();
|
||||
|
||||
m_fontmanager->create_xftdraw(m_pixmap);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,7 +191,7 @@ void renderer::begin() {
|
||||
void renderer::end() {
|
||||
m_log.trace_x("renderer: end");
|
||||
|
||||
m_fontmanager->destroy_xftdraw();
|
||||
m_fontmanager->cleanup();
|
||||
|
||||
#ifdef DEBUG_HINTS
|
||||
debug_hints();
|
||||
@ -356,78 +357,38 @@ void renderer::fill_shift(const int16_t px) {
|
||||
shift_content(px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw character glyph
|
||||
*/
|
||||
void renderer::draw_character(uint16_t character) {
|
||||
m_log.trace_x("renderer: draw_character");
|
||||
|
||||
auto& font = m_fontmanager->match_char(character);
|
||||
|
||||
if (!font) {
|
||||
return m_log.warn("No suitable font found (character=%i)", character);
|
||||
}
|
||||
|
||||
if (font->ptr && font->ptr != m_gcfont) {
|
||||
m_gcfont = font->ptr;
|
||||
m_fontmanager->set_gcontext_font(m_gcontexts.at(gc::FG), m_gcfont);
|
||||
}
|
||||
|
||||
auto width = m_fontmanager->char_width(font, character);
|
||||
auto x = shift_content(width);
|
||||
auto y = m_rect.height / 2 + font->height / 2 - font->descent + font->offset_y;
|
||||
|
||||
if (font->xft != nullptr) {
|
||||
XftDrawString16(m_fontmanager->xftdraw(), m_fontmanager->xftcolor(), font->xft, x, y, &character, 1);
|
||||
} else {
|
||||
uint16_t ucs = ((character >> 8) | (character << 8));
|
||||
draw_util::xcb_poly_text_16_patched(m_connection, m_pixmap, m_gcontexts.at(gc::FG), x, y, 1, &ucs);
|
||||
}
|
||||
|
||||
fill_underline(x, width);
|
||||
fill_overline(x, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw consecutive character glyphs
|
||||
*/
|
||||
void renderer::draw_textstring(const char* text, size_t len) {
|
||||
void renderer::draw_textstring(const uint16_t* text, size_t len) {
|
||||
m_log.trace_x("renderer: draw_textstring(\"%s\")", text);
|
||||
|
||||
for (size_t n = 0; n < len; n++) {
|
||||
vector<uint16_t> chars;
|
||||
chars.emplace_back(text[n]);
|
||||
|
||||
auto& font = m_fontmanager->match_char(chars[0]);
|
||||
vector<uint16_t> chars{text[n]};
|
||||
shared_ptr<font_ref> font{m_fontmanager->match_char(chars[0])};
|
||||
uint8_t width{static_cast<const uint8_t>(m_fontmanager->glyph_width(font, chars[0]) * chars.size())};
|
||||
|
||||
if (!font) {
|
||||
return m_log.warn("No suitable font found (character=%i)", chars[0]);
|
||||
}
|
||||
|
||||
if (font->ptr && font->ptr != m_gcfont) {
|
||||
m_gcfont = font->ptr;
|
||||
m_fontmanager->set_gcontext_font(m_gcontexts.at(gc::FG), m_gcfont);
|
||||
m_log.warn("Could not find glyph for %i", chars[0]);
|
||||
continue;
|
||||
} else if (!width) {
|
||||
m_log.warn("Could not determine glyph width for %i", chars[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (n + 1 < len && text[n + 1] == chars[0]) {
|
||||
chars.emplace_back(text[++n]);
|
||||
chars.emplace_back(text[n++]);
|
||||
}
|
||||
|
||||
auto width = m_fontmanager->char_width(font, chars[0]) * chars.size();
|
||||
width *= chars.size();
|
||||
auto x = shift_content(width);
|
||||
auto y = m_rect.height / 2 + font->height / 2 - font->descent + font->offset_y;
|
||||
|
||||
if (font->xft != nullptr) {
|
||||
XftDrawString16(m_fontmanager->xftdraw(), m_fontmanager->xftcolor(), font->xft, x, y,
|
||||
static_cast<uint16_t*>(chars.data()), chars.size());
|
||||
} else {
|
||||
for (unsigned short& i : chars) {
|
||||
i = ((i >> 8) | (i << 8));
|
||||
if (font->ptr != XCB_NONE && m_gcfont != font->ptr) {
|
||||
m_fontmanager->set_gcontext_font(font, m_gcontexts.at(gc::FG), &m_gcfont);
|
||||
}
|
||||
|
||||
draw_util::xcb_poly_text_16_patched(
|
||||
m_connection, m_pixmap, m_gcontexts.at(gc::FG), x, y, chars.size(), chars.data());
|
||||
}
|
||||
m_fontmanager->drawtext(font, m_pixmap, m_gcontexts.at(gc::FG), x, y, chars.data(), chars.size());
|
||||
|
||||
fill_underline(x, width);
|
||||
fill_overline(x, width);
|
||||
@ -531,7 +492,7 @@ void renderer::debug_hints() {
|
||||
<< cw_size(action.width() - border_width * 2, m_rect.height - border_width * 2)
|
||||
<< cw_pos(action.start_x + num * DEBUG_HINTS_OFFSET_X, m_bar.pos.y + m_rect.y + num * DEBUG_HINTS_OFFSET_Y)
|
||||
<< cw_border(border_width)
|
||||
<< cw_depth(32)
|
||||
<< cw_depth(m_depth)
|
||||
<< cw_visual(m_visual->visual_id)
|
||||
<< cw_params_colormap(m_colormap)
|
||||
<< cw_params_back_pixel(0)
|
||||
@ -713,18 +674,20 @@ bool renderer::on(const action_end& evt) {
|
||||
}
|
||||
|
||||
bool renderer::on(const write_text_ascii& evt) {
|
||||
draw_character(*evt());
|
||||
const uint16_t data[1]{*evt()};
|
||||
draw_textstring(data, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const write_text_unicode& evt) {
|
||||
draw_character(*evt());
|
||||
const uint16_t data[1]{*evt()};
|
||||
draw_textstring(data, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const write_text_string& evt) {
|
||||
string text{*evt()};
|
||||
draw_textstring(text.c_str(), text.size());
|
||||
parser::packet pkt{(*evt())};
|
||||
draw_textstring(pkt.data, pkt.length);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6,55 +6,63 @@
|
||||
#include "utils/factory.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "x11/connection.hpp"
|
||||
#include "x11/draw.hpp"
|
||||
#include "x11/fonts.hpp"
|
||||
#include "x11/xlib.hpp"
|
||||
#include "x11/xutils.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
#define XFT_MAXCHARS (1 << 16)
|
||||
void font_ref::_deleter::operator()(font_ref* font) {
|
||||
font->glyph_widths.clear();
|
||||
font->width_lut.clear();
|
||||
|
||||
array<char, XFT_MAXCHARS> g_xft_widths;
|
||||
array<wchar_t, XFT_MAXCHARS> g_xft_chars;
|
||||
if (font->xft != nullptr) {
|
||||
XftFontClose(xlib::get_display().get(), font->xft);
|
||||
}
|
||||
if (font->ptr != XCB_NONE) {
|
||||
xcb_close_font(xutils::get_connection().get(), font->ptr);
|
||||
}
|
||||
delete font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instance
|
||||
*/
|
||||
font_manager::make_type font_manager::make() {
|
||||
return factory_util::unique<font_manager>(
|
||||
connection::make(), logger::make(), xlib::get_display(), xlib::get_visual());
|
||||
connection::make(), logger::make(), xlib::get_display(), xlib::get_visual(), xlib::create_colormap());
|
||||
}
|
||||
|
||||
void fonttype_deleter::operator()(fonttype* f) {
|
||||
if (f->xft != nullptr) {
|
||||
XftFontClose(xlib::get_display().get(), f->xft);
|
||||
free(f->xft);
|
||||
}
|
||||
if (f->ptr != XCB_NONE) {
|
||||
connection::make().close_font(f->ptr);
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
|
||||
font_manager::font_manager(connection& conn, const logger& logger, shared_ptr<Display>&& dsp, shared_ptr<Visual>&& vis)
|
||||
font_manager::font_manager(
|
||||
connection& conn, const logger& logger, shared_ptr<Display>&& dsp, shared_ptr<Visual>&& vis, Colormap&& cm)
|
||||
: m_connection(conn)
|
||||
, m_logger(logger)
|
||||
, m_display(forward<decltype(dsp)>(dsp))
|
||||
, m_visual(forward<decltype(vis)>(vis)) {
|
||||
m_colormap = xlib::create_colormap(conn.default_screen());
|
||||
, m_visual(forward<decltype(vis)>(vis))
|
||||
, m_colormap(forward<decltype(cm)>(cm)) {
|
||||
if (!XftInit(nullptr) || !XftInitFtLibrary()) {
|
||||
throw application_error("Could not initialize Xft library");
|
||||
}
|
||||
}
|
||||
|
||||
font_manager::~font_manager() {
|
||||
cleanup();
|
||||
if (m_display) {
|
||||
if (m_xftcolor != nullptr) {
|
||||
XftColorFree(m_display.get(), m_visual.get(), m_colormap, m_xftcolor);
|
||||
free(m_xftcolor);
|
||||
if (m_xftcolor_allocated) {
|
||||
XftColorFree(m_display.get(), m_visual.get(), m_colormap, &m_xftcolor);
|
||||
}
|
||||
destroy_xftdraw();
|
||||
XFreeColormap(m_display.get(), m_colormap);
|
||||
}
|
||||
}
|
||||
|
||||
void font_manager::cleanup() {
|
||||
if (m_xftdraw != nullptr) {
|
||||
XftDrawDestroy(m_xftdraw);
|
||||
m_xftdraw = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool font_manager::load(const string& name, int8_t fontindex, int8_t offset_y) {
|
||||
if (fontindex != DEFAULT_FONT_INDEX && m_fonts.find(fontindex) != m_fonts.end()) {
|
||||
m_logger.warn("A font with index '%i' has already been loaded, skip...", fontindex);
|
||||
@ -66,39 +74,40 @@ bool font_manager::load(const string& name, int8_t fontindex, int8_t offset_y) {
|
||||
m_logger.trace("font_manager: Add font '%s' to index '%i'", name, fontindex);
|
||||
}
|
||||
|
||||
fonttype_pointer f{new fonttype_pointer::element_type{}, fonttype_deleter{}};
|
||||
f->offset_y = offset_y;
|
||||
shared_ptr<font_ref> font{new font_ref{}, font_ref::deleter};
|
||||
|
||||
if (open_xcb_font(f, name)) {
|
||||
font->offset_y = offset_y;
|
||||
|
||||
if (open_xcb_font(font, name)) {
|
||||
m_logger.info("Loaded font (xlfd=%s)", name);
|
||||
} else if (f->ptr != XCB_NONE) {
|
||||
m_connection.close_font_checked(f->ptr);
|
||||
f->ptr = XCB_NONE;
|
||||
} else if (font->ptr != XCB_NONE) {
|
||||
m_connection.close_font_checked(font->ptr);
|
||||
font->ptr = XCB_NONE;
|
||||
}
|
||||
|
||||
if (f->ptr == XCB_NONE &&
|
||||
(f->xft = XftFontOpenName(m_display.get(), m_connection.default_screen(), name.c_str())) != nullptr) {
|
||||
f->ascent = f->xft->ascent;
|
||||
f->descent = f->xft->descent;
|
||||
f->height = f->ascent + f->descent;
|
||||
if (font->ptr == XCB_NONE &&
|
||||
(font->xft = XftFontOpenName(m_display.get(), m_connection.default_screen(), name.c_str())) != nullptr) {
|
||||
font->ascent = font->xft->ascent;
|
||||
font->descent = font->xft->descent;
|
||||
font->height = font->ascent + font->descent;
|
||||
|
||||
if (f->xft->pattern != nullptr) {
|
||||
FcChar8* file{nullptr};
|
||||
FcPatternGetString(f->xft->pattern, "file", 0, &file);
|
||||
m_logger.info("Loaded font (pattern=%s, file=%s)", name, file);
|
||||
free(file);
|
||||
if (font->xft->pattern != nullptr) {
|
||||
// XftChar8* file;
|
||||
// XftPatternGetString(font->xft->pattern, "file", 0, &file);
|
||||
// m_logger.info("Loaded font (pattern=%s, file=%s)", name, file);
|
||||
m_logger.info("Loaded font (pattern=%s)", name);
|
||||
} else {
|
||||
m_logger.info("Loaded font (pattern=%s)", name);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->ptr == XCB_NONE && f->xft == nullptr) {
|
||||
if (font->ptr == XCB_NONE && font->xft == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fonts.emplace(make_pair(fontindex, move(f)));
|
||||
m_fonts.emplace(make_pair(fontindex, move(font)));
|
||||
|
||||
int max_height = 0;
|
||||
int max_height{0};
|
||||
|
||||
for (auto& iter : m_fonts) {
|
||||
if (iter.second->height > max_height) {
|
||||
@ -127,112 +136,95 @@ void font_manager::set_preferred_font(int8_t index) {
|
||||
}
|
||||
}
|
||||
|
||||
fonttype_pointer& font_manager::match_char(uint16_t chr) {
|
||||
static fonttype_pointer notfound;
|
||||
if (!m_fonts.empty()) {
|
||||
if (m_fontindex != DEFAULT_FONT_INDEX && size_t(m_fontindex) <= m_fonts.size()) {
|
||||
shared_ptr<font_ref> font_manager::match_char(const uint16_t chr) {
|
||||
if (m_fonts.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_fontindex != DEFAULT_FONT_INDEX && static_cast<size_t>(m_fontindex) <= m_fonts.size()) {
|
||||
auto iter = m_fonts.find(m_fontindex);
|
||||
if (iter != m_fonts.end() && has_glyph(iter->second, chr)) {
|
||||
if (iter == m_fonts.end() || !iter->second) {
|
||||
return {};
|
||||
} else if (has_glyph_xft(iter->second, chr)) {
|
||||
return iter->second;
|
||||
} else if (has_glyph_xcb(iter->second, chr)) {
|
||||
return iter->second;
|
||||
}
|
||||
}
|
||||
for (auto& font : m_fonts) {
|
||||
if (has_glyph(font.second, chr)) {
|
||||
|
||||
for (auto&& font : m_fonts) {
|
||||
if (!font.second) {
|
||||
return {};
|
||||
} else if (has_glyph_xft(font.second, chr)) {
|
||||
return font.second;
|
||||
} else if (has_glyph_xcb(font.second, chr)) {
|
||||
return font.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
return notfound;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
uint8_t font_manager::char_width(fonttype_pointer& font, uint16_t chr) {
|
||||
if (!font) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (font->xft == nullptr) {
|
||||
if (static_cast<size_t>(chr - font->char_min) < font->width_lut.size()) {
|
||||
return font->width_lut[chr - font->char_min].character_width;
|
||||
uint8_t font_manager::glyph_width(const shared_ptr<font_ref>& font, const uint16_t chr) {
|
||||
if (font && font->xft != nullptr) {
|
||||
return glyph_width_xft(move(font), chr);
|
||||
} else if (font && font->ptr != XCB_NONE) {
|
||||
return glyph_width_xcb(move(font), chr);
|
||||
} else {
|
||||
return font->width;
|
||||
}
|
||||
}
|
||||
|
||||
auto index = chr % XFT_MAXCHARS;
|
||||
while (g_xft_chars[index] != 0 && g_xft_chars[index] != chr) {
|
||||
index = (index + 1) % XFT_MAXCHARS;
|
||||
}
|
||||
|
||||
if (!g_xft_chars[index]) {
|
||||
XGlyphInfo gi;
|
||||
FT_UInt glyph = XftCharIndex(m_display.get(), font->xft, static_cast<FcChar32>(chr));
|
||||
XftFontLoadGlyphs(m_display.get(), font->xft, FcFalse, &glyph, 1);
|
||||
XftGlyphExtents(m_display.get(), font->xft, &glyph, 1, &gi);
|
||||
XftFontUnloadGlyphs(m_display.get(), font->xft, &glyph, 1);
|
||||
g_xft_chars[index] = chr;
|
||||
g_xft_widths[index] = gi.xOff;
|
||||
return gi.xOff;
|
||||
} else if (g_xft_chars[index] == chr) {
|
||||
return g_xft_widths[index];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XftColor* font_manager::xftcolor() {
|
||||
return m_xftcolor;
|
||||
}
|
||||
|
||||
XftDraw* font_manager::xftdraw() {
|
||||
return m_xftdraw;
|
||||
}
|
||||
|
||||
void font_manager::create_xftdraw(xcb_pixmap_t pm) {
|
||||
destroy_xftdraw();
|
||||
void font_manager::drawtext(const shared_ptr<font_ref>& font, xcb_pixmap_t pm, xcb_gcontext_t gc, int16_t x, int16_t y,
|
||||
const uint16_t* chars, size_t num_chars) {
|
||||
if (m_xftdraw == nullptr) {
|
||||
m_xftdraw = XftDrawCreate(m_display.get(), pm, m_visual.get(), m_colormap);
|
||||
}
|
||||
|
||||
void font_manager::destroy_xftdraw() {
|
||||
if (m_xftdraw != nullptr) {
|
||||
XftDrawDestroy(m_xftdraw);
|
||||
m_xftdraw = nullptr;
|
||||
if (font->xft != nullptr) {
|
||||
XftDrawString16(m_xftdraw, &m_xftcolor, font->xft, x, y, chars, num_chars);
|
||||
} else if (font->ptr != XCB_NONE) {
|
||||
uint16_t* ucs = static_cast<uint16_t*>(calloc(num_chars, sizeof(uint16_t)));
|
||||
for (size_t i = 0; i < num_chars; i++) {
|
||||
ucs[i] = ((chars[i] >> 8) | (chars[i] << 8));
|
||||
}
|
||||
draw_util::xcb_poly_text_16_patched(m_connection, pm, gc, x, y, num_chars, ucs);
|
||||
}
|
||||
}
|
||||
|
||||
void font_manager::allocate_color(uint32_t color) {
|
||||
XRenderColor x;
|
||||
x.red = color_util::red_channel<uint16_t>(color);
|
||||
x.green = color_util::green_channel<uint16_t>(color);
|
||||
x.blue = color_util::blue_channel<uint16_t>(color);
|
||||
x.alpha = color_util::alpha_channel<uint16_t>(color);
|
||||
// clang-format off
|
||||
XRenderColor x{
|
||||
color_util::red_channel<uint16_t>(color),
|
||||
color_util::green_channel<uint16_t>(color),
|
||||
color_util::blue_channel<uint16_t>(color),
|
||||
color_util::alpha_channel<uint16_t>(color)};
|
||||
// clang-format on
|
||||
allocate_color(x);
|
||||
}
|
||||
|
||||
void font_manager::allocate_color(XRenderColor color) {
|
||||
if (m_xftcolor != nullptr) {
|
||||
XftColorFree(m_display.get(), m_visual.get(), m_colormap, m_xftcolor);
|
||||
free(m_xftcolor);
|
||||
if (m_xftcolor_allocated) {
|
||||
XftColorFree(m_display.get(), m_visual.get(), m_colormap, &m_xftcolor);
|
||||
}
|
||||
|
||||
m_xftcolor = static_cast<XftColor*>(malloc(sizeof(XftColor)));
|
||||
|
||||
if (!XftColorAllocValue(m_display.get(), m_visual.get(), m_colormap, &color, m_xftcolor)) {
|
||||
if (!(m_xftcolor_allocated = XftColorAllocValue(m_display.get(), m_visual.get(), m_colormap, &color, &m_xftcolor))) {
|
||||
m_logger.err("Failed to allocate color");
|
||||
}
|
||||
}
|
||||
|
||||
void font_manager::set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font) {
|
||||
const uint32_t values[1]{font};
|
||||
m_connection.change_gc(gc, XCB_GC_FONT, values);
|
||||
void font_manager::set_gcontext_font(const shared_ptr<font_ref>& font, xcb_gcontext_t gc, xcb_font_t* xcb_font) {
|
||||
const uint32_t val[1]{*xcb_font};
|
||||
m_connection.change_gc(gc, XCB_GC_FONT, val);
|
||||
*xcb_font = font->ptr;
|
||||
}
|
||||
|
||||
bool font_manager::open_xcb_font(fonttype_pointer& fontptr, string fontname) {
|
||||
bool font_manager::open_xcb_font(const shared_ptr<font_ref>& font, string fontname) {
|
||||
try {
|
||||
uint32_t font_id{m_connection.generate_id()};
|
||||
m_connection.open_font_checked(font_id, fontname);
|
||||
|
||||
m_logger.trace("Found X font '%s'", fontname);
|
||||
fontptr->ptr = font_id;
|
||||
font->ptr = font_id;
|
||||
|
||||
auto query = m_connection.query_font(font_id);
|
||||
if (query->char_infos_len == 0) {
|
||||
@ -240,15 +232,15 @@ bool font_manager::open_xcb_font(fonttype_pointer& fontptr, string fontname) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fontptr->descent = query->font_descent;
|
||||
fontptr->height = query->font_ascent + query->font_descent;
|
||||
fontptr->width = query->max_bounds.character_width;
|
||||
fontptr->char_max = query->max_byte1 << 8 | query->max_char_or_byte2;
|
||||
fontptr->char_min = query->min_byte1 << 8 | query->min_char_or_byte2;
|
||||
font->descent = query->font_descent;
|
||||
font->height = query->font_ascent + query->font_descent;
|
||||
font->width = query->max_bounds.character_width;
|
||||
font->char_max = query->max_byte1 << 8 | query->max_char_or_byte2;
|
||||
font->char_min = query->min_byte1 << 8 | query->min_char_or_byte2;
|
||||
|
||||
auto chars = query.char_infos();
|
||||
for (auto it = chars.begin(); it != chars.end(); it++) {
|
||||
fontptr->width_lut.emplace_back(forward<xcb_charinfo_t>(*it));
|
||||
font->width_lut.emplace_back(forward<xcb_charinfo_t>(*it));
|
||||
}
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
@ -258,19 +250,54 @@ bool font_manager::open_xcb_font(fonttype_pointer& fontptr, string fontname) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool font_manager::has_glyph(fonttype_pointer& font, uint16_t chr) {
|
||||
if (font->xft != nullptr) {
|
||||
return static_cast<bool>(XftCharExists(m_display.get(), font->xft, static_cast<FcChar32>(chr)));
|
||||
uint8_t font_manager::glyph_width_xft(const shared_ptr<font_ref>& font, const uint16_t chr) {
|
||||
auto it = font->glyph_widths.find(chr);
|
||||
if (it != font->glyph_widths.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
XGlyphInfo extents{};
|
||||
FT_UInt glyph{XftCharIndex(m_display.get(), font->xft, static_cast<FcChar32>(chr))};
|
||||
|
||||
XftFontLoadGlyphs(m_display.get(), font->xft, FcFalse, &glyph, 1);
|
||||
XftGlyphExtents(m_display.get(), font->xft, &glyph, 1, &extents);
|
||||
XftFontUnloadGlyphs(m_display.get(), font->xft, &glyph, 1);
|
||||
|
||||
font->glyph_widths.emplace_hint(it, chr, extents.xOff); //.emplace_back(chr, extents.xOff);
|
||||
|
||||
return extents.xOff;
|
||||
}
|
||||
|
||||
uint8_t font_manager::glyph_width_xcb(const shared_ptr<font_ref>& font, const uint16_t chr) {
|
||||
if (!font || font->ptr == XCB_NONE) {
|
||||
return 0;
|
||||
} else if (static_cast<size_t>(chr - font->char_min) < font->width_lut.size()) {
|
||||
return font->width_lut[chr - font->char_min].character_width;
|
||||
} else {
|
||||
if (chr < font->char_min || chr > font->char_max) {
|
||||
return false;
|
||||
return font->width;
|
||||
}
|
||||
if (static_cast<size_t>(chr - font->char_min) >= font->width_lut.size()) {
|
||||
return false;
|
||||
}
|
||||
if (font->width_lut[chr - font->char_min].character_width == 0) {
|
||||
|
||||
bool font_manager::has_glyph_xft(const shared_ptr<font_ref>& font, const uint16_t chr) {
|
||||
if (!font || font->xft == nullptr) {
|
||||
return false;
|
||||
} else if (XftCharExists(m_display.get(), font->xft, static_cast<FcChar32>(chr)) == FcFalse) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool font_manager::has_glyph_xcb(const shared_ptr<font_ref>& font, const uint16_t chr) {
|
||||
if (font->ptr == XCB_NONE) {
|
||||
return false;
|
||||
} else if (chr < font->char_min || chr > font->char_max) {
|
||||
return false;
|
||||
} else if (static_cast<size_t>(chr - font->char_min) >= font->width_lut.size()) {
|
||||
return false;
|
||||
} else if (font->width_lut[chr - font->char_min].character_width == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user