2016-11-20 17:04:31 -05:00
|
|
|
#include <X11/Xlib-xcb.h>
|
|
|
|
|
2016-12-09 03:02:47 -05:00
|
|
|
#include "components/logger.hpp"
|
2016-12-13 23:13:59 -05:00
|
|
|
#include "errors.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
#include "utils/color.hpp"
|
2016-12-05 14:41:00 -05:00
|
|
|
#include "utils/factory.hpp"
|
2016-11-20 17:04:31 -05:00
|
|
|
#include "utils/memory.hpp"
|
|
|
|
#include "x11/connection.hpp"
|
|
|
|
#include "x11/fonts.hpp"
|
|
|
|
#include "x11/xlib.hpp"
|
2016-11-26 00:13:20 -05:00
|
|
|
#include "x11/xutils.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
#define XFT_MAXCHARS (1 << 16)
|
|
|
|
|
|
|
|
array<char, XFT_MAXCHARS> g_xft_widths;
|
|
|
|
array<wchar_t, XFT_MAXCHARS> g_xft_chars;
|
|
|
|
|
2016-11-20 17:04:31 -05:00
|
|
|
/**
|
2016-12-09 03:02:47 -05:00
|
|
|
* Create instance
|
2016-11-20 17:04:31 -05:00
|
|
|
*/
|
2016-12-09 03:40:46 -05:00
|
|
|
font_manager::make_type font_manager::make() {
|
2016-12-14 09:09:11 -05:00
|
|
|
return factory_util::unique<font_manager>(
|
|
|
|
connection::make(), logger::make(), xlib::get_display(), xlib::get_visual());
|
2016-11-20 17:04:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void fonttype_deleter::operator()(fonttype* f) {
|
2016-11-25 07:55:15 -05:00
|
|
|
if (f->xft != nullptr) {
|
2016-12-14 09:09:11 -05:00
|
|
|
XftFontClose(xlib::get_display().get(), f->xft);
|
2016-12-13 23:13:59 -05:00
|
|
|
free(f->xft);
|
|
|
|
}
|
|
|
|
if (f->ptr != XCB_NONE) {
|
|
|
|
connection::make().close_font(f->ptr);
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-13 23:13:59 -05:00
|
|
|
delete f;
|
2016-11-20 17:04:31 -05:00
|
|
|
}
|
|
|
|
|
2016-12-14 09:09:11 -05:00
|
|
|
font_manager::font_manager(connection& conn, const logger& logger, shared_ptr<Display>&& dsp, shared_ptr<Visual>&& vis)
|
|
|
|
: m_connection(conn)
|
|
|
|
, m_logger(logger)
|
|
|
|
, m_display(forward<decltype(dsp)>(dsp))
|
|
|
|
, m_visual(forward<decltype(vis)>(vis)) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_colormap = xlib::create_colormap(conn.default_screen());
|
|
|
|
}
|
|
|
|
|
2016-11-20 17:04:31 -05:00
|
|
|
font_manager::~font_manager() {
|
2016-12-14 09:09:11 -05:00
|
|
|
if (m_display) {
|
2016-12-13 23:13:59 -05:00
|
|
|
if (m_xftcolor != nullptr) {
|
2016-12-14 09:09:11 -05:00
|
|
|
XftColorFree(m_display.get(), m_visual.get(), m_colormap, m_xftcolor);
|
2016-12-13 23:13:59 -05:00
|
|
|
free(m_xftcolor);
|
|
|
|
}
|
|
|
|
destroy_xftdraw();
|
2016-12-14 09:09:11 -05:00
|
|
|
XFreeColormap(m_display.get(), m_colormap);
|
2016-12-13 23:13:59 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-11-25 07:55:15 -05:00
|
|
|
bool font_manager::load(const string& name, int8_t fontindex, int8_t offset_y) {
|
2016-11-24 13:24:47 -05:00
|
|
|
if (fontindex != DEFAULT_FONT_INDEX && m_fonts.find(fontindex) != m_fonts.end()) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_logger.warn("A font with index '%i' has already been loaded, skip...", fontindex);
|
|
|
|
return false;
|
2016-11-24 13:24:47 -05:00
|
|
|
} else if (fontindex == DEFAULT_FONT_INDEX) {
|
2016-11-02 15:22:45 -04:00
|
|
|
fontindex = m_fonts.size();
|
2016-11-20 17:04:31 -05:00
|
|
|
m_logger.trace("font_manager: Assign font '%s' to index '%d'", name.c_str(), fontindex);
|
2016-11-02 15:22:45 -04:00
|
|
|
} else {
|
2016-11-20 17:04:31 -05:00
|
|
|
m_logger.trace("font_manager: Add font '%s' to index '%i'", name, fontindex);
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
fonttype_pointer f{new fonttype_pointer::element_type{}, fonttype_deleter{}};
|
|
|
|
f->offset_y = offset_y;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
if (open_xcb_font(f, 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;
|
|
|
|
}
|
|
|
|
|
2016-12-14 09:09:11 -05:00
|
|
|
if (f->ptr == XCB_NONE &&
|
|
|
|
(f->xft = XftFontOpenName(m_display.get(), m_connection.default_screen(), name.c_str())) != nullptr) {
|
2016-12-13 23:13:59 -05:00
|
|
|
f->ascent = f->xft->ascent;
|
|
|
|
f->descent = f->xft->descent;
|
|
|
|
f->height = f->ascent + f->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);
|
|
|
|
} else {
|
|
|
|
m_logger.info("Loaded font (pattern=%s)", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f->ptr == XCB_NONE && f->xft == nullptr) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
m_fonts.emplace(make_pair(fontindex, move(f)));
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
int max_height = 0;
|
|
|
|
|
2016-11-25 07:55:15 -05:00
|
|
|
for (auto& iter : m_fonts) {
|
|
|
|
if (iter.second->height > max_height) {
|
2016-11-02 15:22:45 -04:00
|
|
|
max_height = iter.second->height;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
for (auto& iter : m_fonts) {
|
|
|
|
iter.second->height = max_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-11-21 09:07:00 -05:00
|
|
|
void font_manager::set_preferred_font(int8_t index) {
|
|
|
|
if (index <= 0) {
|
2016-11-24 13:24:47 -05:00
|
|
|
m_fontindex = DEFAULT_FONT_INDEX;
|
2016-11-21 09:07:00 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto&& font : m_fonts) {
|
|
|
|
if (font.first == index) {
|
|
|
|
m_fontindex = index;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
fonttype_pointer& font_manager::match_char(uint16_t chr) {
|
|
|
|
static fonttype_pointer notfound;
|
2016-11-02 15:22:45 -04:00
|
|
|
if (!m_fonts.empty()) {
|
2016-11-24 13:24:47 -05:00
|
|
|
if (m_fontindex != DEFAULT_FONT_INDEX && size_t(m_fontindex) <= m_fonts.size()) {
|
2016-11-02 15:22:45 -04:00
|
|
|
auto iter = m_fonts.find(m_fontindex);
|
2016-11-25 07:55:15 -05:00
|
|
|
if (iter != m_fonts.end() && has_glyph(iter->second, chr)) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return iter->second;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
for (auto& font : m_fonts) {
|
2016-11-25 07:55:15 -05:00
|
|
|
if (has_glyph(font.second, chr)) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return font.second;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return notfound;
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
uint8_t font_manager::char_width(fonttype_pointer& font, uint16_t chr) {
|
2016-11-25 07:55:15 -05:00
|
|
|
if (!font) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return 0;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
if (font->xft == nullptr) {
|
2016-11-25 07:55:15 -05:00
|
|
|
if (static_cast<size_t>(chr - font->char_min) < font->width_lut.size()) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return font->width_lut[chr - font->char_min].character_width;
|
2016-11-25 07:55:15 -05:00
|
|
|
} else {
|
2016-11-02 15:22:45 -04:00
|
|
|
return font->width;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
auto index = chr % XFT_MAXCHARS;
|
2016-12-13 23:13:59 -05:00
|
|
|
while (g_xft_chars[index] != 0 && g_xft_chars[index] != chr) {
|
2016-11-25 07:55:15 -05:00
|
|
|
index = (index + 1) % XFT_MAXCHARS;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
if (!g_xft_chars[index]) {
|
2016-11-02 15:22:45 -04:00
|
|
|
XGlyphInfo gi;
|
2016-12-14 09:09:11 -05:00
|
|
|
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);
|
2016-12-13 23:13:59 -05:00
|
|
|
g_xft_chars[index] = chr;
|
|
|
|
g_xft_widths[index] = gi.xOff;
|
2016-11-02 15:22:45 -04:00
|
|
|
return gi.xOff;
|
2016-12-13 23:13:59 -05:00
|
|
|
} else if (g_xft_chars[index] == chr) {
|
|
|
|
return g_xft_widths[index];
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
XftColor* font_manager::xftcolor() {
|
2016-11-02 15:22:45 -04:00
|
|
|
return m_xftcolor;
|
|
|
|
}
|
|
|
|
|
2016-11-21 09:07:00 -05:00
|
|
|
XftDraw* font_manager::xftdraw() {
|
|
|
|
return m_xftdraw;
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
void font_manager::create_xftdraw(xcb_pixmap_t pm) {
|
|
|
|
destroy_xftdraw();
|
2016-12-14 09:09:11 -05:00
|
|
|
m_xftdraw = XftDrawCreate(m_display.get(), pm, m_visual.get(), m_colormap);
|
2016-11-21 09:07:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void font_manager::destroy_xftdraw() {
|
2016-12-13 23:13:59 -05:00
|
|
|
if (m_xftdraw != nullptr) {
|
|
|
|
XftDrawDestroy(m_xftdraw);
|
|
|
|
m_xftdraw = nullptr;
|
|
|
|
}
|
2016-11-21 09:07:00 -05:00
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
void font_manager::allocate_color(uint32_t color) {
|
2016-11-21 09:07:00 -05:00
|
|
|
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);
|
2016-12-13 23:13:59 -05:00
|
|
|
allocate_color(x);
|
2016-11-21 09:07:00 -05:00
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
void font_manager::allocate_color(XRenderColor color) {
|
|
|
|
if (m_xftcolor != nullptr) {
|
2016-12-14 09:09:11 -05:00
|
|
|
XftColorFree(m_display.get(), m_visual.get(), m_colormap, m_xftcolor);
|
2016-12-13 23:13:59 -05:00
|
|
|
free(m_xftcolor);
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
m_xftcolor = static_cast<XftColor*>(malloc(sizeof(XftColor)));
|
|
|
|
|
2016-12-14 09:09:11 -05:00
|
|
|
if (!XftColorAllocValue(m_display.get(), m_visual.get(), m_colormap, &color, m_xftcolor)) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_logger.err("Failed to allocate color");
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-11-21 09:07:00 -05:00
|
|
|
void font_manager::set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font) {
|
2016-11-02 15:22:45 -04:00
|
|
|
const uint32_t values[1]{font};
|
|
|
|
m_connection.change_gc(gc, XCB_GC_FONT, values);
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
bool font_manager::open_xcb_font(fonttype_pointer& fontptr, string fontname) {
|
2016-11-02 15:22:45 -04:00
|
|
|
try {
|
2016-12-13 23:13:59 -05:00
|
|
|
uint32_t font_id{m_connection.generate_id()};
|
|
|
|
m_connection.open_font_checked(font_id, fontname);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
m_logger.trace("Found X font '%s'", fontname);
|
2016-12-13 23:13:59 -05:00
|
|
|
fontptr->ptr = font_id;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
auto query = m_connection.query_font(font_id);
|
2016-11-02 15:22:45 -04:00
|
|
|
if (query->char_infos_len == 0) {
|
2016-11-20 17:04:31 -05:00
|
|
|
m_logger.warn("X font '%s' does not contain any characters... (Verify the XLFD string)", fontname);
|
2016-11-02 15:22:45 -04:00
|
|
|
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;
|
|
|
|
|
|
|
|
auto chars = query.char_infos();
|
2016-11-25 07:55:15 -05:00
|
|
|
for (auto it = chars.begin(); it != chars.end(); it++) {
|
2016-11-02 15:22:45 -04:00
|
|
|
fontptr->width_lut.emplace_back(forward<xcb_charinfo_t>(*it));
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
return true;
|
|
|
|
} catch (const std::exception& e) {
|
2016-11-20 17:04:31 -05:00
|
|
|
m_logger.trace("font_manager: Could not find X font '%s' (what: %s)", fontname, e.what());
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:13:59 -05:00
|
|
|
bool font_manager::has_glyph(fonttype_pointer& font, uint16_t chr) {
|
2016-11-02 15:22:45 -04:00
|
|
|
if (font->xft != nullptr) {
|
2016-12-14 14:39:40 -05:00
|
|
|
return static_cast<bool>(XftCharExists(m_display.get(), font->xft, static_cast<FcChar32>(chr)));
|
2016-11-02 15:22:45 -04:00
|
|
|
} else {
|
2016-11-25 07:55:15 -05:00
|
|
|
if (chr < font->char_min || chr > font->char_max) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return false;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
|
|
|
if (static_cast<size_t>(chr - font->char_min) >= font->width_lut.size()) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return false;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
|
|
|
if (font->width_lut[chr - font->char_min].character_width == 0) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return false;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS_END
|