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

View File

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

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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})

View File

@ -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++];
}
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<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;
}
/**

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) {
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<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));
}
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;
}

View File

@ -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()) {
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_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() || !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_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 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;
} else {
return font->width;
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);
}
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));
}
}
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;
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 {
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 {
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;
}
}