wip(refactor): Improve parsing and font glyph caching

This commit is contained in:
Michael Carlberg 2016-12-15 17:14:56 +01:00
parent 9f9f438fae
commit b2e8428550
11 changed files with 318 additions and 270 deletions

View File

@ -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: CheckOptions:
- key: modernize-loop-convert.NamingStyle - key: modernize-loop-convert.NamingStyle
value: lower_case value: lower_case

View File

@ -27,10 +27,6 @@
#define STDERR_FILENO 2 #define STDERR_FILENO 2
#endif #endif
#ifdef DEBUG
#include "debug.hpp"
#endif
POLYBAR_NS POLYBAR_NS
namespace placeholders = std::placeholders; namespace placeholders = std::placeholders;

View File

@ -17,6 +17,11 @@ DEFINE_CHILD_ERROR(unclosed_actionblocks, parser_error);
class parser { class parser {
public: public:
struct packet {
uint16_t data[128]{0U};
size_t length{0};
};
explicit parser(signal_emitter& emitter, const bar_settings& bar); explicit parser(signal_emitter& emitter, const bar_settings& bar);
void operator()(string data); void operator()(string data);

View File

@ -32,6 +32,9 @@ class renderer
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts); unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts);
~renderer(); ~renderer();
renderer(const renderer& o) = delete;
renderer& operator=(const renderer& o) = delete;
xcb_window_t window() const; xcb_window_t window() const;
void begin(); void begin();
@ -55,8 +58,7 @@ class renderer
void fill_underline(int16_t x, uint16_t w); void fill_underline(int16_t x, uint16_t w);
void fill_shift(const int16_t px); void fill_shift(const int16_t px);
void draw_character(const uint16_t character); void draw_textstring(const uint16_t* text, size_t len);
void draw_textstring(const char* text, const size_t len);
void begin_action(const mousebtn btn, const string& cmd); void begin_action(const mousebtn btn, const string& cmd);
void end_action(const mousebtn btn); void end_action(const mousebtn btn);

View File

@ -1,21 +1,42 @@
#ifdef DEBUG
#pragma once #pragma once
#ifndef DEBUG
#error "Not a debug build..."
#endif
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
template <class T> #include "common.hpp"
void benchmark_execution_speed(const T& expr) noexcept {
auto start = std::chrono::high_resolution_clock::now(); POLYBAR_NS
expr();
auto finish = std::chrono::high_resolution_clock::now(); namespace debug_util {
std::cout << "execution speed: " << std::chrono::duration_cast<std::chrono::milliseconds>(finish - start).count() template <class T>
<< "ms" << std::endl; 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();
std::cout << "execution speed: " << std::chrono::duration_cast<std::chrono::milliseconds>(finish - start).count()
<< "ms" << std::endl;
}
template <class T>
void execution_speed(const T& expr, size_t iterations) noexcept {
execution_speed([=] { loop(expr, iterations); });
}
template <class T>
void memory_usage(const T& object) noexcept {
std::cout << "memory usage: " << sizeof(object) << "b" << std::endl;
}
} }
template <class T> POLYBAR_NS_END
void benchmark_memory_usage(const T& object) noexcept {
std::cout << "memory usage: " << sizeof(object) << "b" << std::endl;
}
#endif

View File

@ -4,6 +4,7 @@
#include "components/eventloop.hpp" #include "components/eventloop.hpp"
#include "components/ipc.hpp" #include "components/ipc.hpp"
#include "components/parser.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "utils/functional.hpp" #include "utils/functional.hpp"
@ -114,6 +115,8 @@ namespace signals {
} }
namespace parser { namespace parser {
using parser_t = polybar::parser;
DEFINE_VALUE_SIGNAL(70, change_background, uint32_t); DEFINE_VALUE_SIGNAL(70, change_background, uint32_t);
DEFINE_VALUE_SIGNAL(71, change_foreground, uint32_t); DEFINE_VALUE_SIGNAL(71, change_foreground, uint32_t);
DEFINE_VALUE_SIGNAL(72, change_underline, 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(81, action_end, mousebtn);
DEFINE_VALUE_SIGNAL(82, write_text_ascii, uint16_t); DEFINE_VALUE_SIGNAL(82, write_text_ascii, uint16_t);
DEFINE_VALUE_SIGNAL(83, write_text_unicode, 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);
} }
} }

View File

@ -1,7 +1,13 @@
#pragma once #pragma once
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_GLYPH_H
#include <X11/Xft/Xft.h> #include <X11/Xft/Xft.h>
#include <xcb/xcbext.h> #include <xcb/xcbext.h>
#include <unordered_map>
#include "common.hpp" #include "common.hpp"
#include "x11/color.hpp" #include "x11/color.hpp"
@ -9,12 +15,17 @@
POLYBAR_NS POLYBAR_NS
using std::map;
using std::unordered_map;
// fwd // fwd
class connection; class connection;
class logger; class logger;
struct fonttype { struct font_ref {
explicit fonttype() = default; explicit font_ref() = default;
font_ref(const font_ref& o) = delete;
font_ref& operator=(const font_ref& o) = delete;
XftFont* xft{nullptr}; XftFont* xft{nullptr};
xcb_font_t ptr{XCB_NONE}; xcb_font_t ptr{XCB_NONE};
int offset_y{0}; int offset_y{0};
@ -25,57 +36,61 @@ struct fonttype {
uint16_t char_max{0}; uint16_t char_max{0};
uint16_t char_min{0}; uint16_t char_min{0};
vector<xcb_charinfo_t> width_lut{}; vector<xcb_charinfo_t> width_lut{};
}; unordered_map<uint16_t, wchar_t> glyph_widths{};
struct fonttype_deleter { static struct _deleter {
void operator()(fonttype* f); void operator()(font_ref* font);
} deleter;
}; };
using fonttype_pointer = unique_ptr<fonttype, fonttype_deleter>;
class font_manager { class font_manager {
public: public:
using make_type = unique_ptr<font_manager>; using make_type = unique_ptr<font_manager>;
static make_type make(); 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();
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); bool load(const string& name, int8_t fontindex = DEFAULT_FONT_INDEX, int8_t offset_y = 0);
void set_preferred_font(int8_t index); void set_preferred_font(int8_t index);
shared_ptr<font_ref> match_char(const uint16_t chr);
fonttype_pointer& match_char(uint16_t chr); uint8_t glyph_width(const shared_ptr<font_ref>& font, const uint16_t chr);
uint8_t char_width(fonttype_pointer& font, 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);
XftColor* xftcolor();
XftDraw* xftdraw();
void create_xftdraw(xcb_pixmap_t pm);
void destroy_xftdraw();
void allocate_color(uint32_t color); void allocate_color(uint32_t color);
void allocate_color(XRenderColor 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: protected:
bool open_xcb_font(fonttype_pointer& fontptr, string fontname); bool open_xcb_font(const shared_ptr<font_ref>& font, string fontname);
bool has_glyph(fonttype_pointer& font, uint16_t chr);
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: private:
connection& m_connection; connection& m_connection;
const logger& m_logger; const logger& m_logger;
shared_ptr<Display> m_display{nullptr}; shared_ptr<Display> m_display;
shared_ptr<Visual> m_visual{nullptr}; shared_ptr<Visual> m_visual;
Colormap m_colormap{}; 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}; int8_t m_fontindex{DEFAULT_FONT_INDEX};
XftColor* m_xftcolor{nullptr};
XftDraw* m_xftdraw{nullptr}; XftDraw* m_xftdraw{nullptr};
XftColor m_xftcolor{};
bool m_xftcolor_allocated{false};
}; };
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -140,6 +140,7 @@ make_executable(${PROJECT_NAME} SOURCES
target_link_libraries(${PROJECT_NAME} Threads::Threads) target_link_libraries(${PROJECT_NAME} Threads::Threads)
target_compile_definitions(${PROJECT_NAME} PUBLIC target_compile_definitions(${PROJECT_NAME} PUBLIC
${X11_Xft_DEFINITIONS}
${X11_XCB_DEFINITIONS} ${X11_XCB_DEFINITIONS}
${XCB_DEFINITIONS}) ${XCB_DEFINITIONS})

View File

@ -5,6 +5,7 @@
#include "events/signal.hpp" #include "events/signal.hpp"
#include "events/signal_emitter.hpp" #include "events/signal_emitter.hpp"
#include "utils/math.hpp" #include "utils/math.hpp"
#include "utils/memory.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
POLYBAR_NS POLYBAR_NS
@ -29,8 +30,7 @@ void parser::operator()(string data) {
} else if ((pos = data.find("%{")) != string::npos) { } else if ((pos = data.find("%{")) != string::npos) {
data.erase(0, text(data.substr(0, pos))); data.erase(0, text(data.substr(0, pos)));
} else { } else {
text(move(data)); data.erase(0, text(data.substr(0)));
return;
} }
} }
@ -155,20 +155,32 @@ void parser::codeblock(string&& data) {
* Process text contents * Process text contents
*/ */
size_t parser::text(string&& data) { 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) { if (utf[0] < 0x80) {
// grab all consecutive ascii chars size_t pos{0};
size_t next_tag = data.find("%{");
if (next_tag != string::npos) { // grab consecutive ascii chars
data.erase(next_tag); 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 } 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))}); m_sig.emit(write_text_unicode{static_cast<uint16_t>((utf[0] & 0x1f) << 6 | (utf[1] & 0x3f))});
return 2; return 2;
@ -185,10 +197,13 @@ size_t parser::text(string&& data) {
} else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence } else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)}); m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 6; 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;
} }
/** /**

View File

@ -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) { if ((m_visual = m_connection.visual_type(m_connection.screen(), 32)) == nullptr) {
m_log.err("No 32-bit TrueColor visual found..."); 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; 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_log.trace("renderer: Allocate colormap");
m_colormap = m_connection.generate_id(); 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) m_window = winspec(m_connection)
<< cw_size(m_bar.size) << cw_size(m_bar.size)
<< cw_pos(m_bar.pos) << cw_pos(m_bar.pos)
<< cw_depth(32) << cw_depth(m_depth)
<< cw_visual(m_visual->visual_id) << cw_visual(m_visual->visual_id)
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT) << cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
<< cw_params_back_pixel(0) << 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_log.trace("renderer: Allocate window pixmap");
m_pixmap = m_connection.generate_id(); 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"); m_log.trace("renderer: Allocate graphic contexts");
{ {
@ -180,8 +183,6 @@ void renderer::begin() {
m_currentx = 0; m_currentx = 0;
m_attributes = 0; m_attributes = 0;
m_actions.clear(); m_actions.clear();
m_fontmanager->create_xftdraw(m_pixmap);
} }
/** /**
@ -190,7 +191,7 @@ void renderer::begin() {
void renderer::end() { void renderer::end() {
m_log.trace_x("renderer: end"); m_log.trace_x("renderer: end");
m_fontmanager->destroy_xftdraw(); m_fontmanager->cleanup();
#ifdef DEBUG_HINTS #ifdef DEBUG_HINTS
debug_hints(); debug_hints();
@ -356,79 +357,39 @@ void renderer::fill_shift(const int16_t px) {
shift_content(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 * 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); m_log.trace_x("renderer: draw_textstring(\"%s\")", text);
for (size_t n = 0; n < len; n++) { for (size_t n = 0; n < len; n++) {
vector<uint16_t> chars; vector<uint16_t> chars{text[n]};
chars.emplace_back(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())};
auto& font = m_fontmanager->match_char(chars[0]);
if (!font) { if (!font) {
return m_log.warn("No suitable font found (character=%i)", chars[0]); m_log.warn("Could not find glyph for %i", chars[0]);
} continue;
} else if (!width) {
if (font->ptr && font->ptr != m_gcfont) { m_log.warn("Could not determine glyph width for %i", chars[0]);
m_gcfont = font->ptr; continue;
m_fontmanager->set_gcontext_font(m_gcontexts.at(gc::FG), m_gcfont);
} }
while (n + 1 < len && text[n + 1] == chars[0]) { 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 x = shift_content(width);
auto y = m_rect.height / 2 + font->height / 2 - font->descent + font->offset_y; auto y = m_rect.height / 2 + font->height / 2 - font->descent + font->offset_y;
if (font->xft != nullptr) { if (font->ptr != XCB_NONE && m_gcfont != font->ptr) {
XftDrawString16(m_fontmanager->xftdraw(), m_fontmanager->xftcolor(), font->xft, x, y, m_fontmanager->set_gcontext_font(font, m_gcontexts.at(gc::FG), &m_gcfont);
static_cast<uint16_t*>(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());
} }
m_fontmanager->drawtext(font, m_pixmap, m_gcontexts.at(gc::FG), x, y, chars.data(), chars.size());
fill_underline(x, width); fill_underline(x, width);
fill_overline(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_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_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_border(border_width)
<< cw_depth(32) << cw_depth(m_depth)
<< cw_visual(m_visual->visual_id) << cw_visual(m_visual->visual_id)
<< cw_params_colormap(m_colormap) << cw_params_colormap(m_colormap)
<< cw_params_back_pixel(0) << cw_params_back_pixel(0)
@ -713,18 +674,20 @@ bool renderer::on(const action_end& evt) {
} }
bool renderer::on(const write_text_ascii& evt) { bool renderer::on(const write_text_ascii& evt) {
draw_character(*evt()); const uint16_t data[1]{*evt()};
draw_textstring(data, 1);
return true; return true;
} }
bool renderer::on(const write_text_unicode& evt) { bool renderer::on(const write_text_unicode& evt) {
draw_character(*evt()); const uint16_t data[1]{*evt()};
draw_textstring(data, 1);
return true; return true;
} }
bool renderer::on(const write_text_string& evt) { bool renderer::on(const write_text_string& evt) {
string text{*evt()}; parser::packet pkt{(*evt())};
draw_textstring(text.c_str(), text.size()); draw_textstring(pkt.data, pkt.length);
return true; return true;
} }

View File

@ -6,55 +6,63 @@
#include "utils/factory.hpp" #include "utils/factory.hpp"
#include "utils/memory.hpp" #include "utils/memory.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/draw.hpp"
#include "x11/fonts.hpp" #include "x11/fonts.hpp"
#include "x11/xlib.hpp" #include "x11/xlib.hpp"
#include "x11/xutils.hpp" #include "x11/xutils.hpp"
POLYBAR_NS 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; if (font->xft != nullptr) {
array<wchar_t, XFT_MAXCHARS> g_xft_chars; 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 * Create instance
*/ */
font_manager::make_type font_manager::make() { font_manager::make_type font_manager::make() {
return factory_util::unique<font_manager>( 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) { font_manager::font_manager(
if (f->xft != nullptr) { connection& conn, const logger& logger, shared_ptr<Display>&& dsp, shared_ptr<Visual>&& vis, Colormap&& cm)
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)
: m_connection(conn) : m_connection(conn)
, m_logger(logger) , m_logger(logger)
, m_display(forward<decltype(dsp)>(dsp)) , m_display(forward<decltype(dsp)>(dsp))
, m_visual(forward<decltype(vis)>(vis)) { , m_visual(forward<decltype(vis)>(vis))
m_colormap = xlib::create_colormap(conn.default_screen()); , m_colormap(forward<decltype(cm)>(cm)) {
if (!XftInit(nullptr) || !XftInitFtLibrary()) {
throw application_error("Could not initialize Xft library");
}
} }
font_manager::~font_manager() { font_manager::~font_manager() {
cleanup();
if (m_display) { if (m_display) {
if (m_xftcolor != nullptr) { if (m_xftcolor_allocated) {
XftColorFree(m_display.get(), m_visual.get(), m_colormap, m_xftcolor); XftColorFree(m_display.get(), m_visual.get(), m_colormap, &m_xftcolor);
free(m_xftcolor);
} }
destroy_xftdraw();
XFreeColormap(m_display.get(), m_colormap); 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) { 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()) { 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); 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); m_logger.trace("font_manager: Add font '%s' to index '%i'", name, fontindex);
} }
fonttype_pointer f{new fonttype_pointer::element_type{}, fonttype_deleter{}}; shared_ptr<font_ref> font{new font_ref{}, font_ref::deleter};
f->offset_y = offset_y;
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); m_logger.info("Loaded font (xlfd=%s)", name);
} else if (f->ptr != XCB_NONE) { } else if (font->ptr != XCB_NONE) {
m_connection.close_font_checked(f->ptr); m_connection.close_font_checked(font->ptr);
f->ptr = XCB_NONE; font->ptr = XCB_NONE;
} }
if (f->ptr == XCB_NONE && if (font->ptr == XCB_NONE &&
(f->xft = XftFontOpenName(m_display.get(), m_connection.default_screen(), name.c_str())) != nullptr) { (font->xft = XftFontOpenName(m_display.get(), m_connection.default_screen(), name.c_str())) != nullptr) {
f->ascent = f->xft->ascent; font->ascent = font->xft->ascent;
f->descent = f->xft->descent; font->descent = font->xft->descent;
f->height = f->ascent + f->descent; font->height = font->ascent + font->descent;
if (f->xft->pattern != nullptr) { if (font->xft->pattern != nullptr) {
FcChar8* file{nullptr}; // XftChar8* file;
FcPatternGetString(f->xft->pattern, "file", 0, &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, file=%s)", name, file);
free(file); m_logger.info("Loaded font (pattern=%s)", name);
} else { } else {
m_logger.info("Loaded font (pattern=%s)", name); 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; 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) { for (auto& iter : m_fonts) {
if (iter.second->height > max_height) { 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) { shared_ptr<font_ref> font_manager::match_char(const uint16_t chr) {
static fonttype_pointer notfound; if (m_fonts.empty()) {
if (!m_fonts.empty()) { return {};
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)) { if (m_fontindex != DEFAULT_FONT_INDEX && static_cast<size_t>(m_fontindex) <= m_fonts.size()) {
return iter->second; auto iter = m_fonts.find(m_fontindex);
} if (iter == m_fonts.end() || !iter->second) {
} return {};
for (auto& font : m_fonts) { } else if (has_glyph_xft(iter->second, chr)) {
if (has_glyph(font.second, chr)) { return iter->second;
return font.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) { uint8_t font_manager::glyph_width(const shared_ptr<font_ref>& font, const uint16_t chr) {
if (!font) { 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; return 0;
} }
}
if (font->xft == nullptr) { void font_manager::drawtext(const shared_ptr<font_ref>& font, xcb_pixmap_t pm, xcb_gcontext_t gc, int16_t x, int16_t y,
if (static_cast<size_t>(chr - font->char_min) < font->width_lut.size()) { const uint16_t* chars, size_t num_chars) {
return font->width_lut[chr - font->char_min].character_width; if (m_xftdraw == nullptr) {
} else { m_xftdraw = XftDrawCreate(m_display.get(), pm, m_visual.get(), m_colormap);
return font->width; }
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);
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();
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;
} }
} }
void font_manager::allocate_color(uint32_t color) { void font_manager::allocate_color(uint32_t color) {
XRenderColor x; // clang-format off
x.red = color_util::red_channel<uint16_t>(color); XRenderColor x{
x.green = color_util::green_channel<uint16_t>(color); color_util::red_channel<uint16_t>(color),
x.blue = color_util::blue_channel<uint16_t>(color); color_util::green_channel<uint16_t>(color),
x.alpha = color_util::alpha_channel<uint16_t>(color); color_util::blue_channel<uint16_t>(color),
color_util::alpha_channel<uint16_t>(color)};
// clang-format on
allocate_color(x); allocate_color(x);
} }
void font_manager::allocate_color(XRenderColor color) { void font_manager::allocate_color(XRenderColor color) {
if (m_xftcolor != nullptr) { if (m_xftcolor_allocated) {
XftColorFree(m_display.get(), m_visual.get(), m_colormap, m_xftcolor); XftColorFree(m_display.get(), m_visual.get(), m_colormap, &m_xftcolor);
free(m_xftcolor);
} }
m_xftcolor = static_cast<XftColor*>(malloc(sizeof(XftColor))); if (!(m_xftcolor_allocated = XftColorAllocValue(m_display.get(), m_visual.get(), m_colormap, &color, &m_xftcolor))) {
if (!XftColorAllocValue(m_display.get(), m_visual.get(), m_colormap, &color, m_xftcolor)) {
m_logger.err("Failed to allocate color"); m_logger.err("Failed to allocate color");
} }
} }
void font_manager::set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font) { void font_manager::set_gcontext_font(const shared_ptr<font_ref>& font, xcb_gcontext_t gc, xcb_font_t* xcb_font) {
const uint32_t values[1]{font}; const uint32_t val[1]{*xcb_font};
m_connection.change_gc(gc, XCB_GC_FONT, values); 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 { try {
uint32_t font_id{m_connection.generate_id()}; uint32_t font_id{m_connection.generate_id()};
m_connection.open_font_checked(font_id, fontname); m_connection.open_font_checked(font_id, fontname);
m_logger.trace("Found X font '%s'", 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); auto query = m_connection.query_font(font_id);
if (query->char_infos_len == 0) { if (query->char_infos_len == 0) {
@ -240,15 +232,15 @@ bool font_manager::open_xcb_font(fonttype_pointer& fontptr, string fontname) {
return false; return false;
} }
fontptr->descent = query->font_descent; font->descent = query->font_descent;
fontptr->height = query->font_ascent + query->font_descent; font->height = query->font_ascent + query->font_descent;
fontptr->width = query->max_bounds.character_width; font->width = query->max_bounds.character_width;
fontptr->char_max = query->max_byte1 << 8 | query->max_char_or_byte2; font->char_max = query->max_byte1 << 8 | query->max_char_or_byte2;
fontptr->char_min = query->min_byte1 << 8 | query->min_char_or_byte2; font->char_min = query->min_byte1 << 8 | query->min_char_or_byte2;
auto chars = query.char_infos(); auto chars = query.char_infos();
for (auto it = chars.begin(); it != chars.end(); it++) { 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; return true;
} catch (const std::exception& e) { } catch (const std::exception& e) {
@ -258,19 +250,54 @@ bool font_manager::open_xcb_font(fonttype_pointer& fontptr, string fontname) {
return false; return false;
} }
bool font_manager::has_glyph(fonttype_pointer& font, uint16_t chr) { uint8_t font_manager::glyph_width_xft(const shared_ptr<font_ref>& font, const uint16_t chr) {
if (font->xft != nullptr) { auto it = font->glyph_widths.find(chr);
return static_cast<bool>(XftCharExists(m_display.get(), font->xft, static_cast<FcChar32>(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 {
return font->width;
}
}
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 { } else {
if (chr < font->char_min || chr > font->char_max) {
return false;
}
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) {
return false;
}
return true; return true;
} }
} }