From b2e8428550f475fabd9a325f90cce67c42f5f988 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Thu, 15 Dec 2016 17:14:56 +0100 Subject: [PATCH] wip(refactor): Improve parsing and font glyph caching --- .clang-tidy | 2 +- include/common.hpp | 4 - include/components/parser.hpp | 5 + include/components/renderer.hpp | 6 +- include/debug.hpp | 49 ++++-- include/events/signal.hpp | 5 +- include/x11/fonts.hpp | 67 ++++--- src/CMakeLists.txt | 1 + src/components/parser.cpp | 45 +++-- src/components/renderer.cpp | 105 ++++------- src/x11/fonts.cpp | 299 +++++++++++++++++--------------- 11 files changed, 318 insertions(+), 270 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index a584c6ce..967f1fd6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -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 diff --git a/include/common.hpp b/include/common.hpp index d9fe66fd..8417cbdf 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -27,10 +27,6 @@ #define STDERR_FILENO 2 #endif -#ifdef DEBUG -#include "debug.hpp" -#endif - POLYBAR_NS namespace placeholders = std::placeholders; diff --git a/include/components/parser.hpp b/include/components/parser.hpp index 7fb11f75..ea10488e 100644 --- a/include/components/parser.hpp +++ b/include/components/parser.hpp @@ -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); diff --git a/include/components/renderer.hpp b/include/components/renderer.hpp index 5c5d9c90..20fa32ba 100644 --- a/include/components/renderer.hpp +++ b/include/components/renderer.hpp @@ -32,6 +32,9 @@ class renderer unique_ptr font_manager, const bar_settings& bar, const vector& 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); diff --git a/include/debug.hpp b/include/debug.hpp index d4b7436a..35b6b43f 100644 --- a/include/debug.hpp +++ b/include/debug.hpp @@ -1,21 +1,42 @@ -#ifdef DEBUG #pragma once +#ifndef DEBUG +#error "Not a debug build..." +#endif + #include #include -template -void benchmark_execution_speed(const T& expr) noexcept { - auto start = std::chrono::high_resolution_clock::now(); - expr(); - auto finish = std::chrono::high_resolution_clock::now(); - std::cout << "execution speed: " << std::chrono::duration_cast(finish - start).count() - << "ms" << std::endl; +#include "common.hpp" + +POLYBAR_NS + +namespace debug_util { + template + void loop(const T& expr, size_t iterations) noexcept { + while (iterations--) { + expr(); + } + } + + template + void execution_speed(const T& expr) noexcept { + auto start = std::chrono::high_resolution_clock::now(); + expr(); + auto finish = std::chrono::high_resolution_clock::now(); + std::cout << "execution speed: " << std::chrono::duration_cast(finish - start).count() + << "ms" << std::endl; + } + + template + void execution_speed(const T& expr, size_t iterations) noexcept { + execution_speed([=] { loop(expr, iterations); }); + } + + template + void memory_usage(const T& object) noexcept { + std::cout << "memory usage: " << sizeof(object) << "b" << std::endl; + } } -template -void benchmark_memory_usage(const T& object) noexcept { - std::cout << "memory usage: " << sizeof(object) << "b" << std::endl; -} - -#endif +POLYBAR_NS_END diff --git a/include/events/signal.hpp b/include/events/signal.hpp index e0494127..9b15f37f 100644 --- a/include/events/signal.hpp +++ b/include/events/signal.hpp @@ -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); } } diff --git a/include/x11/fonts.hpp b/include/x11/fonts.hpp index 4952d119..5361ef3a 100644 --- a/include/x11/fonts.hpp +++ b/include/x11/fonts.hpp @@ -1,7 +1,13 @@ #pragma once +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_GLYPH_H + #include #include +#include #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 width_lut{}; -}; + unordered_map glyph_widths{}; -struct fonttype_deleter { - void operator()(fonttype* f); + static struct _deleter { + void operator()(font_ref* font); + } deleter; }; -using fonttype_pointer = unique_ptr; - class font_manager { public: using make_type = unique_ptr; static make_type make(); - explicit font_manager(connection& conn, const logger& logger, shared_ptr&& dsp, shared_ptr&& vis); + explicit font_manager( + connection& conn, const logger& logger, shared_ptr&& dsp, shared_ptr&& 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 match_char(const uint16_t chr); + uint8_t glyph_width(const shared_ptr& font, const uint16_t chr); + void drawtext(const shared_ptr& 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, 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, string fontname); + + uint8_t glyph_width_xft(const shared_ptr& font, const uint16_t chr); + uint8_t glyph_width_xcb(const shared_ptr& font, const uint16_t chr); + + bool has_glyph_xft(const shared_ptr& font, const uint16_t chr); + bool has_glyph_xcb(const shared_ptr& font, const uint16_t chr); private: connection& m_connection; const logger& m_logger; - shared_ptr m_display{nullptr}; - shared_ptr m_visual{nullptr}; - Colormap m_colormap{}; + shared_ptr m_display; + shared_ptr m_visual; + Colormap m_colormap; - std::map m_fonts; + map> 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3033c5b0..376195d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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}) diff --git a/src/components/parser.cpp b/src/components/parser.cpp index 438b4a94..8a1adc06 100644 --- a/src/components/parser.cpp +++ b/src/components/parser.cpp @@ -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(const_cast(data.c_str())); + const uint8_t* utf{reinterpret_cast(&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++]; + } + + if (!len) { + break; + } + + pkt.length = len; + m_sig.emit(write_text_string{move(pkt)}); } - size_t n = 0; - while (utf[n] != '\0' && utf[++n] < 0x80) { - ; + + 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((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(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; } /** diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp index 4f1fee26..2bdaaeec 100644 --- a/src/components/renderer.cpp +++ b/src/components/renderer.cpp @@ -47,14 +47,17 @@ 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..."); + + 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; } - 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_log.trace("renderer: Allocate colormap"); m_colormap = m_connection.generate_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,79 +357,39 @@ 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 chars; - chars.emplace_back(text[n]); - - auto& font = m_fontmanager->match_char(chars[0]); + vector chars{text[n]}; + shared_ptr font{m_fontmanager->match_char(chars[0])}; + uint8_t width{static_cast(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(chars.data()), chars.size()); - } else { - for (unsigned short& i : chars) { - i = ((i >> 8) | (i << 8)); - } - - draw_util::xcb_poly_text_16_patched( - m_connection, m_pixmap, m_gcontexts.at(gc::FG), x, y, chars.size(), chars.data()); + if (font->ptr != XCB_NONE && m_gcfont != font->ptr) { + m_fontmanager->set_gcontext_font(font, m_gcontexts.at(gc::FG), &m_gcfont); } + 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; } diff --git a/src/x11/fonts.cpp b/src/x11/fonts.cpp index b079d856..4f492159 100644 --- a/src/x11/fonts.cpp +++ b/src/x11/fonts.cpp @@ -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 g_xft_widths; -array 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( - 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&& dsp, shared_ptr&& vis) +font_manager::font_manager( + connection& conn, const logger& logger, shared_ptr&& dsp, shared_ptr&& vis, Colormap&& cm) : m_connection(conn) , m_logger(logger) , m_display(forward(dsp)) - , m_visual(forward(vis)) { - m_colormap = xlib::create_colormap(conn.default_screen()); + , m_visual(forward(vis)) + , m_colormap(forward(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{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()) { - auto iter = m_fonts.find(m_fontindex); - if (iter != m_fonts.end() && has_glyph(iter->second, chr)) { - return iter->second; - } - } - for (auto& font : m_fonts) { - if (has_glyph(font.second, chr)) { - return font.second; - } +shared_ptr font_manager::match_char(const uint16_t chr) { + if (m_fonts.empty()) { + return {}; + } + + if (m_fontindex != DEFAULT_FONT_INDEX && static_cast(m_fontindex) <= m_fonts.size()) { + auto iter = m_fonts.find(m_fontindex); + 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; } } - return notfound; + + 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 {}; } -uint8_t font_manager::char_width(fonttype_pointer& font, uint16_t chr) { - if (!font) { +uint8_t font_manager::glyph_width(const shared_ptr& 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 0; } +} - if (font->xft == nullptr) { - if (static_cast(chr - font->char_min) < font->width_lut.size()) { - return font->width_lut[chr - font->char_min].character_width; - } else { - return font->width; +void font_manager::drawtext(const shared_ptr& 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); + } + 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(calloc(num_chars, sizeof(uint16_t))); + for (size_t i = 0; i < num_chars; i++) { + ucs[i] = ((chars[i] >> 8) | (chars[i] << 8)); } - } - - 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(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(); - 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; + 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(color); - x.green = color_util::green_channel(color); - x.blue = color_util::blue_channel(color); - x.alpha = color_util::alpha_channel(color); + // clang-format off + XRenderColor x{ + color_util::red_channel(color), + color_util::green_channel(color), + color_util::blue_channel(color), + color_util::alpha_channel(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(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, 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, 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(*it)); + font->width_lut.emplace_back(forward(*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(XftCharExists(m_display.get(), font->xft, static_cast(chr))); +uint8_t font_manager::glyph_width_xft(const shared_ptr& 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(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, const uint16_t chr) { + if (!font || font->ptr == XCB_NONE) { + return 0; + } else if (static_cast(chr - font->char_min) < font->width_lut.size()) { + return font->width_lut[chr - font->char_min].character_width; + } else { + return font->width; + } +} + +bool font_manager::has_glyph_xft(const shared_ptr& font, const uint16_t chr) { + if (!font || font->xft == nullptr) { + return false; + } else if (XftCharExists(m_display.get(), font->xft, static_cast(chr)) == FcFalse) { + return false; + } else { + return true; + } +} + +bool font_manager::has_glyph_xcb(const shared_ptr& 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(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 { - if (chr < font->char_min || chr > font->char_max) { - return false; - } - if (static_cast(chr - font->char_min) >= font->width_lut.size()) { - return false; - } - if (font->width_lut[chr - font->char_min].character_width == 0) { - return false; - } return true; } }