1
0
Fork 0
mirror of https://github.com/polybar/polybar.git synced 2024-11-25 13:55:47 -05:00

refactor: Separate render component

This commit is contained in:
Michael Carlberg 2016-11-21 15:07:00 +01:00
parent c1162960cc
commit 25e33b6aab
16 changed files with 985 additions and 924 deletions

View file

@ -2,7 +2,6 @@
#include "common.hpp" #include "common.hpp"
#include "components/config.hpp" #include "components/config.hpp"
#include "components/logger.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "utils/concurrency.hpp" #include "utils/concurrency.hpp"
#include "utils/throttle.hpp" #include "utils/throttle.hpp"
@ -15,11 +14,12 @@ POLYBAR_NS
// fwd // fwd
class tray_manager; class tray_manager;
class font_manager; class logger;
class renderer;
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify> { class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify> {
public: public:
explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr<font_manager> font_manager, explicit bar(connection& conn, const config& config, const logger& logger,
unique_ptr<tray_manager> tray_manager); unique_ptr<tray_manager> tray_manager);
~bar(); ~bar();
@ -32,14 +32,9 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
void parse(string data, bool force = false); void parse(string data, bool force = false);
protected: protected:
void flush();
void refresh_window(); void refresh_window();
void load_fonts();
void configure_geom();
void create_monitor(); void create_monitor();
void create_window(); void configure_geom();
void create_pixmap();
void create_gcontexts();
void restack_window(); void restack_window();
void map_window(); void map_window();
void set_wmhints(); void set_wmhints();
@ -51,62 +46,35 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
void handle(const evt::expose& evt); void handle(const evt::expose& evt);
void handle(const evt::property_notify& evt); void handle(const evt::property_notify& evt);
void on_alignment_change(alignment align); void on_alignment_change(const alignment align);
void on_attribute_set(attribute attr); void on_attribute_set(const attribute attr);
void on_attribute_unset(attribute attr); void on_attribute_unset(const attribute attr);
void on_attribute_toggle(attribute attr); void on_attribute_toggle(const attribute attr);
void on_action_block_open(mousebtn btn, string cmd); void on_action_block_open(const mousebtn btn, string cmd);
void on_action_block_close(mousebtn btn); void on_action_block_close(const mousebtn btn);
void on_color_change(gc gc_, color color_); void on_color_change(const gc gc_, uint32_t color);
void on_font_change(int index); void on_font_change(int index);
void on_pixel_offset(int px); void on_pixel_offset(int px);
void on_tray_report(uint16_t slots); void on_tray_report(uint16_t slots);
void draw_background();
void draw_border(border border_);
void draw_lines(int x, int w);
int draw_shift(int x, int chr_width);
void draw_character(uint16_t character);
void draw_textstring(const char* text, size_t len);
private: private:
connection& m_connection; connection& m_connection;
const config& m_conf; const config& m_conf;
const logger& m_log; const logger& m_log;
unique_ptr<font_manager> m_fontmanager;
unique_ptr<tray_manager> m_tray; unique_ptr<tray_manager> m_tray;
unique_ptr<renderer> m_renderer;
concurrency_util::spin_lock m_lock;
throttle_util::throttle_t m_throttler;
xcb_screen_t* m_screen;
rect m_screensize{};
xcb_visualtype_t* m_visual;
window m_window{m_connection, m_connection.generate_id()};
colormap m_colormap{m_connection, m_connection.generate_id()};
pixmap m_pixmap{m_connection, m_connection.generate_id()};
// xcb_gcontext_t m_root_gc{0};
// graphics_util::root_pixmap m_rootpixmap;
bar_settings m_opts; bar_settings m_opts;
map<border, border_settings> m_borders; xcb_window_t m_window;
map<gc, gcontext> m_gcontexts; xcb_screen_t* m_screen;
vector<action_block> m_actions; size m_screensize{};
bool m_sinkattached{false};
string m_lastinput;
stateflag m_sinkattached{false}; alignment m_trayalign{alignment::NONE};
uint8_t m_trayclients{0};
alignment m_traypos{alignment::NONE}; std::mutex m_mutex;
uint16_t m_trayclients{0};
string m_prevdata;
int m_xpos{0};
int m_attributes{0};
xcb_font_t m_gcfont{0};
XftDraw* m_xftdraw;
}; };
di::injector<unique_ptr<bar>> configure_bar(); di::injector<unique_ptr<bar>> configure_bar();

View file

@ -1,22 +1,25 @@
#pragma once #pragma once
#include "common.hpp" #include "common.hpp"
#include "components/signals.hpp"
POLYBAR_NS POLYBAR_NS
struct bar_settings;
enum class attribute : uint8_t;
enum class mousebtn : uint8_t;
DEFINE_ERROR(unrecognized_token); DEFINE_ERROR(unrecognized_token);
class parser { class parser {
public: public:
explicit parser(const bar_settings& bar) : m_bar(bar) {} explicit parser(const bar_settings& bar);
void operator()(string data); void operator()(string data);
void codeblock(string data); void codeblock(string data);
size_t text(string data); size_t text(string data);
protected: protected:
color parse_color(string s, color fallback = color{0}); uint32_t parse_color(string s, uint32_t fallback = 0);
int parse_fontindex(string s); int8_t parse_fontindex(string s);
attribute parse_attr(const char s); attribute parse_attr(const char s);
mousebtn parse_action_btn(string data); mousebtn parse_action_btn(string data);
string parse_action_cmd(string data); string parse_action_cmd(string data);

View file

@ -0,0 +1,83 @@
#pragma once
#include "common.hpp"
#include "components/types.hpp"
#include "x11/types.hpp"
POLYBAR_NS
class connection;
class font_manager;
class logger;
class renderer {
public:
explicit renderer(connection& conn, const logger& logger, unique_ptr<font_manager> font_manager,
const bar_settings& bar, const vector<string>& fonts);
xcb_window_t window() const;
void begin();
void end();
void redraw();
void reserve_space(edge side, uint16_t w);
void set_background(const gc gcontext, const uint32_t color);
void set_foreground(const gc gcontext, const uint32_t color);
void set_fontindex(const uint8_t font);
void set_alignment(const alignment align);
void set_attribute(const attribute attr, const bool state);
void fill_background();
void fill_border(const map<edge, border_settings>& borders, edge border);
void fill_overline(int16_t x, uint16_t w);
void fill_underline(int16_t x, uint16_t w);
void draw_character(uint16_t character);
void draw_textstring(const char* text, size_t len);
int16_t shift_content(int16_t x, int16_t shift_x);
int16_t shift_content(int16_t shift_x);
void begin_action(const mousebtn btn, const string& cmd);
void end_action(const mousebtn btn);
const vector<action_block> get_actions();
protected:
void debughints();
private:
connection& m_connection;
const logger& m_log;
unique_ptr<font_manager> m_fontmanager;
const bar_settings& m_bar;
xcb_window_t m_window;
xcb_colormap_t m_colormap;
xcb_visualtype_t* m_visual;
// xcb_gcontext_t m_gcontext;
xcb_pixmap_t m_pixmap;
map<gc, xcb_gcontext_t> m_gcontexts;
map<alignment, xcb_pixmap_t> m_pixmaps;
vector<action_block> m_actions;
// bool m_autosize{false};
int m_currentx{0};
int m_attributes{0};
alignment m_alignment{alignment::NONE};
xcb_font_t m_gcfont{0};
uint32_t m_background{0};
uint32_t m_foreground{0};
edge m_reserve_at{edge::NONE};
uint16_t m_reserve;
};
di::injector<unique_ptr<renderer>> configure_renderer(const bar_settings& bar, const vector<string>& fonts);
POLYBAR_NS_END

View file

@ -8,12 +8,11 @@ POLYBAR_NS
// fwd decl {{{ // fwd decl {{{
enum class mousebtn; enum class mousebtn : uint8_t;
enum class syntaxtag; enum class syntaxtag : uint8_t;
enum class alignment; enum class alignment : uint8_t;
enum class attribute; enum class attribute : uint8_t;
enum class gc; enum class gc : uint8_t;
class color;
// }}} // }}}
@ -23,27 +22,26 @@ class color;
namespace g_signals { namespace g_signals {
namespace bar { namespace bar {
extern callback<string> action_click; extern callback<string> action_click;
extern callback<bool> visibility_change; extern callback<const bool> visibility_change;
} }
namespace parser { namespace parser {
extern callback<alignment> alignment_change; extern callback<const alignment> alignment_change;
extern callback<attribute> attribute_set; extern callback<const attribute> attribute_set;
extern callback<attribute> attribute_unset; extern callback<const attribute> attribute_unset;
extern callback<attribute> attribute_toggle; extern callback<const mousebtn, string> action_block_open;
extern callback<mousebtn, string> action_block_open; extern callback<const mousebtn> action_block_close;
extern callback<mousebtn> action_block_close; extern callback<const gc, const uint32_t> color_change;
extern callback<gc, color> color_change; extern callback<const int8_t> font_change;
extern callback<int> font_change; extern callback<const int16_t> pixel_offset;
extern callback<int> pixel_offset; extern callback<const uint16_t> ascii_text_write;
extern callback<uint16_t> ascii_text_write; extern callback<const uint16_t> unicode_text_write;
extern callback<uint16_t> unicode_text_write; extern callback<const char*, const size_t> string_write;
extern callback<const char*, size_t> string_write;
} }
namespace tray { namespace tray {
extern callback<uint16_t> report_slotcount; extern callback<const uint16_t> report_slotcount;
extern callback<uint32_t> clear_bg; extern callback<const uint32_t> clear_bg;
} }
} }

View file

@ -6,19 +6,13 @@
POLYBAR_NS POLYBAR_NS
enum class border { NONE = 0, TOP, BOTTOM, LEFT, RIGHT, ALL }; enum class edge : uint8_t { NONE = 0, TOP, BOTTOM, LEFT, RIGHT, ALL };
enum class alignment { NONE = 0, LEFT, CENTER, RIGHT }; enum class alignment : uint8_t { NONE = 0, LEFT, CENTER, RIGHT };
enum class syntaxtag { NONE = 0, A, B, F, T, U, O, R, o, u }; enum class syntaxtag : uint8_t { NONE = 0, A, B, F, T, U, O, R, o, u };
enum class attribute { NONE = 0, o = 2, u = 4 }; enum class attribute : uint8_t { NONE = 0, o = 1 << 0, u = 1 << 1 };
enum class mousebtn { NONE = 0, LEFT, MIDDLE, RIGHT, SCROLL_UP, SCROLL_DOWN }; enum class mousebtn : uint8_t { NONE = 0, LEFT, MIDDLE, RIGHT, SCROLL_UP, SCROLL_DOWN };
enum class gc { NONE = 0, BG, FG, OL, UL, BT, BB, BL, BR }; enum class gc : uint8_t { NONE = 0, BG, FG, OL, UL, BT, BB, BL, BR };
enum class strut : uint16_t {
struct rect {
uint16_t w{0};
uint16_t h{0};
};
enum class strut {
LEFT = 0, LEFT = 0,
RIGHT, RIGHT,
TOP, TOP,
@ -33,80 +27,73 @@ enum class strut {
BOTTOM_END_X, BOTTOM_END_X,
}; };
struct strut_margins { struct position {
uint16_t t;
uint16_t b;
uint16_t l;
uint16_t r;
};
struct bar_settings {
bar_settings() = default;
string locale;
int16_t x{0}; int16_t x{0};
int16_t y{0}; int16_t y{0};
uint16_t width{0}; };
uint16_t height{0};
int16_t offset_y{0}; struct size {
int16_t offset_x{0}; uint16_t w{0};
uint16_t h{0};
};
uint16_t padding_left{0}; struct side_values {
uint16_t padding_right{0}; uint16_t left{0};
uint16_t right{0};
};
int16_t module_margin_left{0}; struct edge_values {
int16_t module_margin_right{2}; uint16_t left{0};
uint16_t right{0};
int16_t lineheight{0}; uint16_t top{0};
int16_t spacing{1}; uint16_t bottom{0};
string separator;
color background{g_colorwhite};
color foreground{g_colorblack};
color linecolor{g_colorblack};
alignment align{alignment::RIGHT};
bool bottom{false};
bool dock{false};
monitor_t monitor;
string wmname;
int16_t vertical_mid{0};
strut_margins margins;
string geom() {
char buffer[32]{
'\0',
};
snprintf(buffer, sizeof(buffer), "%dx%d+%d+%d", width, height, x, y);
return string{*buffer};
}
}; };
struct border_settings { struct border_settings {
border_settings() = default; uint32_t color{0xFF000000};
polybar::color color{g_colorblack};
uint16_t size{0}; uint16_t size{0};
}; };
struct bar_settings {
monitor_t monitor;
edge origin{edge::BOTTOM};
size size{0, 0};
position pos{0, 0};
position offset{0, 0};
position center{0, 0};
side_values padding{0, 0};
side_values margin{0, 0};
side_values module_margin{0, 2};
edge_values strut{0, 0, 0, 0};
uint32_t background{0xFFFFFFFF};
uint32_t foreground{0xFF0000FF};
uint32_t linecolor{0xFF000000};
map<edge, border_settings> borders;
int8_t lineheight{0};
int8_t spacing{1};
string separator;
string wmname;
string locale;
bool force_docking{false};
};
struct action_block { struct action_block {
action_block() = default; alignment align{alignment::NONE};
mousebtn button{mousebtn::NONE};
string command;
int16_t start_x{0}; int16_t start_x{0};
int16_t end_x{0}; int16_t end_x{0};
alignment align; mousebtn button{mousebtn::NONE};
string command;
bool active{true}; bool active{true};
#if DEBUG and DRAW_CLICKABLE_AREA_HINTS #if DEBUG and DRAW_CLICKABLE_AREA_HINTS
xcb_window_t clickable_area; xcb_window_t hint;
#endif #endif
}; };
struct wmsettings_bspwm {};
POLYBAR_NS_END POLYBAR_NS_END

View file

@ -54,7 +54,7 @@ namespace color_util {
return (a << 24) | (r << 16) | (g << 8) | b; return (a << 24) | (r << 16) | (g << 8) | b;
} }
template <typename T = uint8_t> template <typename T>
string hex(uint32_t color) { string hex(uint32_t color) {
char s[12]; char s[12];
size_t len = 0; size_t len = 0;
@ -85,9 +85,9 @@ namespace color_util {
return hex; return hex;
} }
inline uint32_t parse(string hex) { inline uint32_t parse(string hex, uint32_t fallback = 0) {
if ((hex = parse_hex(hex)).empty()) if ((hex = parse_hex(hex)).empty())
return 0U; return fallback;
return std::strtoul(&hex[1], nullptr, 16); return std::strtoul(&hex[1], nullptr, 16);
} }
} }

View file

@ -42,36 +42,40 @@ class font_manager {
explicit font_manager(connection& conn, const logger& logger); explicit font_manager(connection& conn, const logger& logger);
~font_manager(); ~font_manager();
void set_preferred_font(int index); bool load(string name, int8_t fontindex = -1, int8_t offset_y = 0);
bool load(string name, int fontindex = -1, int offset_y = 0); void set_preferred_font(int8_t index);
font_t& match_char(uint16_t chr); font_t& match_char(uint16_t chr);
uint8_t char_width(font_t& font, uint16_t chr);
int char_width(font_t& font, uint16_t chr);
XftColor xftcolor(); XftColor xftcolor();
XftDraw* xftdraw();
XftDraw* create_xftdraw(xcb_pixmap_t pm, xcb_colormap_t cm);
void destroy_xftdraw();
void allocate_color(uint32_t color, bool initial_alloc = false);
void allocate_color(XRenderColor color, bool initial_alloc = false); void allocate_color(XRenderColor color, bool initial_alloc = false);
void set_gcontext_font(gcontext& gc, xcb_font_t font); void set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font);
protected: protected:
bool open_xcb_font(font_t& fontptr, string fontname); bool open_xcb_font(font_t& fontptr, string fontname);
bool has_glyph(font_t& font, uint16_t chr); bool has_glyph(font_t& font, uint16_t chr);
private: private:
connection& m_connection; connection& m_connection;
const logger& m_logger; const logger& m_logger;
Display* m_display = nullptr; Display* m_display{nullptr};
Visual* m_visual = nullptr; Visual* m_visual{nullptr};
Colormap m_colormap; Colormap m_colormap{};
map<int, font_t> m_fonts; map<uint8_t, font_t> m_fonts;
int m_fontindex = -1; int8_t m_fontindex{-1};
XftColor m_xftcolor;
XftColor m_xftcolor{};
XftDraw* m_xftdraw{nullptr};
}; };
di::injector<unique_ptr<font_manager>> configure_font_manager(); di::injector<unique_ptr<font_manager>> configure_font_manager();

File diff suppressed because it is too large Load diff

View file

@ -248,7 +248,7 @@ void builder::font_close(bool force) {
void builder::background(string color) { void builder::background(string color) {
if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) { if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) {
color = "#" + color.substr(color.length() - 2); color = "#" + color.substr(color.length() - 2);
auto bg = m_bar.background.source(); auto bg = color_util::hex<uint16_t>(m_bar.background);
color += bg.substr(bg.length() - (bg.length() < 6 ? 3 : 6)); color += bg.substr(bg.length() - (bg.length() < 6 ? 3 : 6));
} else if (color.length() >= 7 && color == "#" + string(color.length() - 1, color[1])) { } else if (color.length() >= 7 && color == "#" + string(color.length() - 1, color[1])) {
color = color.substr(0, 4); color = color.substr(0, 4);
@ -279,7 +279,7 @@ void builder::color(string color_) {
auto color(color_); auto color(color_);
if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) { if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) {
color = "#" + color.substr(color.length() - 2); color = "#" + color.substr(color.length() - 2);
auto fg = m_bar.foreground.source(); auto fg = color_util::hex<uint16_t>(m_bar.foreground);
color += fg.substr(fg.length() - (fg.length() < 6 ? 3 : 6)); color += fg.substr(fg.length() - (fg.length() < 6 ? 3 : 6));
} else if (color.length() >= 7 && color == "#" + string(color.length() - 1, color[1])) { } else if (color.length() >= 7 && color == "#" + string(color.length() - 1, color[1])) {
color = color.substr(0, 4); color = color.substr(0, 4);
@ -299,7 +299,7 @@ void builder::color(string color_) {
void builder::color_alpha(string alpha_) { void builder::color_alpha(string alpha_) {
auto alpha(alpha_); auto alpha(alpha_);
string val = m_bar.foreground.source(); string val = color_util::hex<uint16_t>(m_bar.foreground);
if (alpha.find("#") == std::string::npos) { if (alpha.find("#") == std::string::npos) {
alpha = "#" + alpha; alpha = "#" + alpha;
} }

View file

@ -2,11 +2,13 @@
#include <csignal> #include <csignal>
#include <mutex> #include <mutex>
#include "x11/color.hpp"
#include "components/bar.hpp" #include "components/bar.hpp"
#include "components/config.hpp"
#include "components/controller.hpp" #include "components/controller.hpp"
#include "components/eventloop.hpp"
#include "components/ipc.hpp"
#include "components/logger.hpp"
#include "components/signals.hpp"
#include "modules/backlight.hpp" #include "modules/backlight.hpp"
#include "modules/battery.hpp" #include "modules/battery.hpp"
#include "modules/bspwm.hpp" #include "modules/bspwm.hpp"
@ -22,12 +24,6 @@
#include "modules/text.hpp" #include "modules/text.hpp"
#include "modules/xbacklight.hpp" #include "modules/xbacklight.hpp"
#include "modules/xwindow.hpp" #include "modules/xwindow.hpp"
#include "components/config.hpp"
#include "components/eventloop.hpp"
#include "components/ipc.hpp"
#include "components/logger.hpp"
#include "components/signals.hpp"
#include "utils/process.hpp" #include "utils/process.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
@ -44,7 +40,7 @@
#include "modules/volume.hpp" #include "modules/volume.hpp"
#endif #endif
#if not (ENABLE_I3 && ENABLE_MPD && ENABLE_NETWORK && ENABLE_ALSA) #if not(ENABLE_I3 && ENABLE_MPD && ENABLE_NETWORK && ENABLE_ALSA)
#include "modules/unsupported.hpp" #include "modules/unsupported.hpp"
#endif #endif
@ -499,14 +495,16 @@ void controller::on_unrecognized_action(string input) {
* Callback for module content update * Callback for module content update
*/ */
void controller::on_update() { void controller::on_update() {
const bar_settings& bar{m_bar->settings()};
string contents{""}; string contents{""};
string separator{m_bar->settings().separator}; string separator{bar.separator};
string padding_left(m_bar->settings().padding_left, ' '); string padding_left(bar.padding.left, ' ');
string padding_right(m_bar->settings().padding_right, ' '); string padding_right(bar.padding.right, ' ');
auto margin_left = m_bar->settings().module_margin_left; auto margin_left = bar.module_margin.left;
auto margin_right = m_bar->settings().module_margin_right; auto margin_right = bar.module_margin.right;
for (const auto& block : m_eventloop->modules()) { for (const auto& block : m_eventloop->modules()) {
string block_contents; string block_contents;

View file

@ -1,12 +1,13 @@
#include "x11/color.hpp"
#include "components/types.hpp"
#include "components/parser.hpp" #include "components/parser.hpp"
#include "components/signals.hpp"
#include "components/types.hpp"
#include "utils/math.hpp" #include "utils/math.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
POLYBAR_NS POLYBAR_NS
parser::parser(const bar_settings& bar) : m_bar(bar) {}
/** /**
* Parse input data * Parse input data
*/ */
@ -111,11 +112,6 @@ void parser::codeblock(string data) {
g_signals::parser::attribute_unset(parse_attr(value[0])); g_signals::parser::attribute_unset(parse_attr(value[0]));
break; break;
case '!':
if (g_signals::parser::attribute_toggle)
g_signals::parser::attribute_toggle(parse_attr(value[0]));
break;
case 'A': case 'A':
if (isdigit(data[0]) || data[0] == ':') { if (isdigit(data[0]) || data[0] == ':') {
value = parse_action_cmd(data); value = parse_action_cmd(data);
@ -172,8 +168,7 @@ size_t parser::text(string data) {
return 2; return 2;
} else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence } else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence
if (g_signals::parser::unicode_text_write) if (g_signals::parser::unicode_text_write)
g_signals::parser::unicode_text_write( g_signals::parser::unicode_text_write((utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f));
(utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f));
return 3; return 3;
} else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence } else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence
if (g_signals::parser::unicode_text_write) if (g_signals::parser::unicode_text_write)
@ -197,20 +192,26 @@ size_t parser::text(string data) {
/** /**
* TODO: docstring * TODO: docstring
*/ */
color parser::parse_color(string s, color fallback) { uint32_t parser::parse_color(string s, uint32_t fallback) {
if (s.empty() || s == "-") uint32_t color{0};
if (s.empty() || s[0] == '-' || (color = color_util::parse(s, fallback)) == fallback)
return fallback; return fallback;
return color::parse(s, fallback); return color_util::premultiply_alpha(color);
} }
/** /**
* TODO: docstring * TODO: docstring
*/ */
int parser::parse_fontindex(string s) { int8_t parser::parse_fontindex(string s) {
if (s.empty() || s == "-") if (s.empty() || s == "-") {
return -1; return -1;
char* p = (char*)s.c_str(); }
return std::strtoul(p, &p, 10);
try {
return std::stoul(s.c_str(), nullptr, 10);
} catch (const std::invalid_argument& err) {
return -1;
}
} }
/** /**
@ -246,8 +247,11 @@ mousebtn parser::parse_action_btn(string data) {
* TODO: docstring * TODO: docstring
*/ */
string parser::parse_action_cmd(string data) { string parser::parse_action_cmd(string data) {
auto start = string_util::find_nth(data, 0, ":", 1); size_t start, end;
auto end = string_util::find_nth(data, 0, ":", 2); if ((start = data.find(':')) == string::npos)
return "";
if ((end = data.find(':', start + 1)) == string::npos)
return "";
return string_util::trim(data.substr(start, end), ':'); return string_util::trim(data.substr(start, end), ':');
} }

499
src/components/renderer.cpp Normal file
View file

@ -0,0 +1,499 @@
#include "components/renderer.hpp"
#include "components/logger.hpp"
#include "x11/connection.hpp"
#include "x11/draw.hpp"
#include "x11/fonts.hpp"
#include "x11/winspec.hpp"
POLYBAR_NS
/**
* Configure injection module
*/
di::injector<unique_ptr<renderer>> configure_renderer(const bar_settings& bar, const vector<string>& fonts) {
// clang-format off
return di::make_injector(
di::bind<>().to(bar),
di::bind<>().to(fonts),
configure_connection(),
configure_logger(),
configure_font_manager());
// clang-format on
}
renderer::renderer(connection& conn, const logger& logger, unique_ptr<font_manager> font_manager,
const bar_settings& bar, const vector<string>& fonts)
: m_connection(conn), m_log(logger), m_fontmanager(forward<decltype(font_manager)>(font_manager)), m_bar(bar) {
auto screen = m_connection.screen();
m_log.trace("bar: Get true color visual");
m_visual = m_connection.visual_type(screen, 32).get();
m_log.trace("bar: Create colormap");
m_colormap = m_connection.generate_id();
m_connection.create_colormap(XCB_COLORMAP_ALLOC_NONE, m_colormap, screen->root, m_visual->visual_id);
m_window = m_connection.generate_id();
m_log.trace("bar: Create window %s", m_connection.id(m_window));
{
uint32_t mask{0};
uint32_t values[16]{0};
xcb_params_cw_t params;
// clang-format off
XCB_AUX_ADD_PARAM(&mask, &params, back_pixel, 0);
XCB_AUX_ADD_PARAM(&mask, &params, border_pixel, 0);
XCB_AUX_ADD_PARAM(&mask, &params, backing_store, XCB_BACKING_STORE_WHEN_MAPPED);
XCB_AUX_ADD_PARAM(&mask, &params, colormap, m_colormap);
XCB_AUX_ADD_PARAM(&mask, &params, override_redirect, m_bar.force_docking);
XCB_AUX_ADD_PARAM(&mask, &params, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS);
// clang-format on
xutils::pack_values(mask, &params, values);
m_connection.create_window(32, m_window, screen->root, m_bar.pos.x, m_bar.pos.y, m_bar.size.w, m_bar.size.h, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, m_visual->visual_id, mask, values);
}
m_pixmap = m_connection.generate_id();
m_log.trace("bar: Create pixmap (xid=%s)", m_connection.id(m_pixmap));
m_connection.create_pixmap(32, m_pixmap, m_window, m_bar.size.w, m_bar.size.h);
m_log.trace("bar: Create gcontexts");
{
// clang-format off
vector<uint32_t> colors {
m_bar.background,
m_bar.foreground,
m_bar.linecolor,
m_bar.linecolor,
m_bar.borders.at(edge::TOP).color,
m_bar.borders.at(edge::BOTTOM).color,
m_bar.borders.at(edge::LEFT).color,
m_bar.borders.at(edge::RIGHT).color,
};
// clang-format on
for (int i = 1; i <= 8; i++) {
uint32_t mask{0};
uint32_t value_list[32]{0};
xcb_params_gc_t params;
XCB_AUX_ADD_PARAM(&mask, &params, foreground, colors[i - 1]);
XCB_AUX_ADD_PARAM(&mask, &params, graphics_exposures, 0);
xutils::pack_values(mask, &params, value_list);
m_gcontexts.emplace(gc(i), m_connection.generate_id());
m_log.trace("bar: Create gcontext (gc=%i, xid=%s)", i, m_connection.id(m_gcontexts.at(gc(i))));
m_connection.create_gc(m_gcontexts.at(gc(i)), m_pixmap, mask, value_list);
}
}
m_log.trace("bar: Load fonts");
{
auto fonts_loaded = false;
auto fontindex = 0;
if (fonts.empty())
m_log.warn("No fonts specified, using fallback font \"fixed\"");
for (auto f : fonts) {
fontindex++;
vector<string> fd = string_util::split(f, ';');
string pattern{fd[0]};
int offset{0};
if (fd.size() > 1)
offset = std::stoi(fd[1], 0, 10);
if (m_fontmanager->load(pattern, fontindex, offset))
fonts_loaded = true;
else
m_log.warn("Unable to load font '%s'", fd[0]);
}
if (!fonts_loaded && !fonts.empty())
m_log.warn("Unable to load fonts, using fallback font \"fixed\"");
if (!fonts_loaded && !m_fontmanager->load("fixed"))
throw application_error("Unable to load fonts");
m_fontmanager->allocate_color(m_bar.foreground, true);
}
}
xcb_window_t renderer::window() const {
return m_window;
}
void renderer::begin() {
#if DEBUG and DRAW_CLICKABLE_AREA_HINTS
for (auto&& action : m_actions) {
m_connection.destroy_window(action.hint);
}
#endif
m_currentx = m_bar.borders.at(edge::LEFT).size;
m_attributes = 0;
m_actions.clear();
fill_background();
m_fontmanager->create_xftdraw(m_pixmap, m_colormap);
}
void renderer::end() {
xcb_rectangle_t rect{0, 0, m_bar.size.w, m_bar.size.h};
if (m_reserve_at == edge::LEFT) {
rect.x += m_reserve;
rect.width -= m_reserve;
} else if (m_reserve_at == edge::RIGHT) {
rect.width -= m_reserve;
}
fill_border(m_bar.borders, edge::ALL);
m_connection.copy_area(
m_pixmap, m_window, m_gcontexts.at(gc::FG), rect.x, rect.y, rect.x, rect.y, rect.width, rect.height);
m_connection.flush();
m_fontmanager->destroy_xftdraw();
#ifdef DEBUG
debughints();
#endif
m_reserve = 0;
m_reserve_at = edge::NONE;
}
void renderer::redraw() {}
void renderer::reserve_space(edge side, uint16_t w) {
m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast<uint8_t>(side), w);
m_reserve = w;
m_reserve_at = side;
}
void renderer::set_background(const gc gcontext, const uint32_t color) {
if (color == m_background)
return;
m_log.trace_x("renderer: set_background(%i, #%08x)", static_cast<uint8_t>(gcontext), color);
m_connection.change_gc(m_gcontexts.at(gcontext), XCB_GC_BACKGROUND, &color);
m_background = color;
}
void renderer::set_foreground(const gc gcontext, const uint32_t color) {
if (color == m_foreground)
return;
m_log.trace_x("renderer: set_foreground(%i, #%08x)", static_cast<uint8_t>(gcontext), color);
m_connection.change_gc(m_gcontexts.at(gcontext), XCB_GC_FOREGROUND, &color);
if (gcontext == gc::FG)
m_fontmanager->allocate_color(color);
else if (gcontext == gc::BG)
shift_content(0);
m_foreground = color;
}
void renderer::set_fontindex(const uint8_t font) {
m_log.trace_x("renderer: set_fontindex(%i)", static_cast<uint8_t>(font));
m_fontmanager->set_preferred_font(font);
}
void renderer::set_alignment(const alignment align) {
if (align == m_alignment) {
return;
} else if (align == alignment::LEFT) {
m_currentx = m_bar.borders.at(edge::LEFT).size;
} else if (align == alignment::RIGHT) {
m_currentx = m_bar.borders.at(edge::RIGHT).size;
} else {
m_currentx = 0;
}
if (align == alignment::LEFT && m_reserve_at == edge::LEFT) {
m_currentx += m_reserve;
} else if (align == alignment::RIGHT && m_reserve_at == edge::RIGHT) {
m_currentx += m_reserve;
}
m_log.trace_x("renderer: set_alignment(%i)", static_cast<uint8_t>(align));
m_alignment = align;
}
void renderer::set_attribute(const attribute attr, bool state) {
m_log.trace_x("renderer: set_attribute(%i, %i)", static_cast<uint8_t>(attr), state);
if (state) {
m_attributes |= static_cast<uint8_t>(attr);
} else {
m_attributes ^= static_cast<uint8_t>(attr);
}
}
void renderer::fill_background() {
xcb_rectangle_t rect{0, 0, m_bar.size.w, m_bar.size.h};
if (m_reserve_at == edge::LEFT) {
rect.x += m_reserve;
rect.width -= m_reserve;
} else if (m_reserve_at == edge::RIGHT) {
rect.width -= m_reserve;
}
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), rect.x, rect.y, rect.width, rect.height);
}
void renderer::fill_border(const map<edge, border_settings>& borders, edge border) {
for (auto&& b : borders) {
if (border != edge::ALL && b.first != border)
continue;
if (b.second.size <= 0)
continue;
switch (b.first) {
case edge::TOP:
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BT), borders.at(edge::LEFT).size, 0,
m_bar.size.w - borders.at(edge::LEFT).size - borders.at(edge::RIGHT).size, borders.at(edge::TOP).size);
break;
case edge::BOTTOM:
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BB), borders.at(edge::LEFT).size,
m_bar.size.h - borders.at(edge::BOTTOM).size,
m_bar.size.w - borders.at(edge::LEFT).size - borders.at(edge::RIGHT).size, borders.at(edge::BOTTOM).size);
break;
case edge::LEFT:
draw_util::fill(
m_connection, m_pixmap, m_gcontexts.at(gc::BL), 0, 0, borders.at(edge::LEFT).size, m_bar.size.h);
break;
case edge::RIGHT:
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BR), m_bar.size.w - borders.at(edge::RIGHT).size, 0,
borders.at(edge::RIGHT).size, m_bar.size.h);
break;
default:
break;
}
}
}
void renderer::fill_overline(int16_t x, uint16_t w) {
if (!m_bar.lineheight || !(m_attributes & static_cast<int>(attribute::o)))
return;
draw_util::fill(
m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, m_bar.borders.at(edge::TOP).size, w, m_bar.lineheight);
}
void renderer::fill_underline(int16_t x, uint16_t w) {
if (!m_bar.lineheight || !(m_attributes & static_cast<int>(attribute::u)))
return;
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x,
m_bar.size.h - m_bar.borders.at(edge::BOTTOM).size - m_bar.lineheight, w, m_bar.lineheight);
}
void renderer::draw_character(uint16_t character) {
auto& font = m_fontmanager->match_char(character);
if (!font) {
return;
}
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);
// Avoid odd glyph width's for center-aligned text
// since it breaks the positioning of clickable area's
if (m_alignment == alignment::CENTER && width % 2)
width++;
auto x = shift_content(width);
auto y = m_bar.center.y + font->height / 2 - font->descent + font->offset_y;
if (font->xft != nullptr) {
auto color = m_fontmanager->xftcolor();
XftDrawString16(m_fontmanager->xftdraw(), &color, 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);
}
}
void renderer::draw_textstring(const char* text, size_t len) {
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]);
if (!font) {
return;
}
if (font->ptr && font->ptr != m_gcfont) {
m_gcfont = font->ptr;
m_fontmanager->set_gcontext_font(m_gcontexts.at(gc::FG), m_gcfont);
}
while (n + 1 < len && text[n + 1] == chars[0]) {
chars.emplace_back(text[++n]);
}
// TODO: cache
auto width = m_fontmanager->char_width(font, chars[0]) * chars.size();
// Avoid odd glyph width's for center-aligned text
// since it breaks the positioning of clickable area's
if (m_alignment == alignment::CENTER && width % 2)
width++;
auto x = shift_content(width);
auto y = m_bar.center.y + font->height / 2 - font->descent + font->offset_y;
if (font->xft != nullptr) {
auto color = m_fontmanager->xftcolor();
const FcChar16* drawchars = static_cast<const FcChar16*>(chars.data());
XftDrawString16(m_fontmanager->xftdraw(), &color, font->xft, x, y, drawchars, chars.size());
} else {
for (size_t i = 0; i < chars.size(); i++) {
chars[i] = ((chars[i] >> 8) | (chars[i] << 8));
}
draw_util::xcb_poly_text_16_patched(
m_connection, m_pixmap, m_gcontexts.at(gc::FG), x, y, chars.size(), chars.data());
}
}
}
int16_t renderer::shift_content(int16_t x, int16_t shift_x) {
int delta = shift_x;
if (m_alignment == alignment::CENTER) {
int base_x = m_bar.size.w;
base_x -= m_bar.borders.at(edge::RIGHT).size;
base_x /= 2;
base_x += m_bar.borders.at(edge::LEFT).size;
m_connection.copy_area(
m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), base_x - x / 2, 0, base_x - (x + shift_x) / 2, 0, x, m_bar.size.h);
x = base_x - (x + shift_x) / 2 + x;
delta /= 2;
} else if (m_alignment == alignment::RIGHT) {
m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), m_bar.size.w - x, 0, m_bar.size.w - x - shift_x,
0, x, m_bar.size.h);
x = m_bar.size.w - shift_x - m_bar.borders.at(edge::RIGHT).size;
if (m_reserve_at == edge::RIGHT)
x -= m_reserve;
}
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), x, 0, m_bar.size.w - x, m_bar.size.h);
// Translate pos of clickable areas
if (m_alignment != alignment::LEFT) {
for (auto&& action : m_actions) {
if (action.active || action.align != m_alignment)
continue;
action.start_x -= delta;
action.end_x -= delta;
}
}
m_currentx += shift_x;
fill_underline(x, shift_x);
fill_overline(x, shift_x);
return x;
}
int16_t renderer::shift_content(int16_t shift_x) {
return shift_content(m_currentx, shift_x);
}
void renderer::begin_action(const mousebtn btn, const string& cmd) {
action_block action{};
action.button = btn;
action.align = m_alignment;
action.start_x = m_currentx;
action.command = string_util::replace_all(cmd, ":", "\\:");
action.active = true;
if (action.button == mousebtn::NONE)
action.button = mousebtn::LEFT;
m_log.trace_x("renderer: begin_action(%i, %s)", static_cast<uint8_t>(action.button), cmd.c_str());
m_actions.emplace_back(action);
}
void renderer::end_action(const mousebtn btn) {
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
if (!action->active || action->button != btn)
continue;
m_log.trace_x("renderer: end_action(%i, %s)", static_cast<uint8_t>(btn), action->command.c_str());
action->active = false;
if (action->align == alignment::LEFT) {
action->end_x = m_currentx;
} else if (action->align == alignment::CENTER) {
int base_x{m_bar.size.w};
int clickable_width{m_currentx - action->start_x};
base_x -= m_bar.borders.at(edge::RIGHT).size;
base_x /= 2;
base_x += m_bar.borders.at(edge::LEFT).size;
action->start_x = base_x - clickable_width / 2 + action->start_x / 2;
action->end_x = action->start_x + clickable_width;
} else if (action->align == alignment::RIGHT) {
int base_x{m_bar.size.w - m_bar.borders.at(edge::RIGHT).size};
if (m_reserve_at == edge::RIGHT)
base_x -= m_reserve;
action->start_x = base_x - m_currentx + action->start_x;
action->end_x = base_x;
}
return;
}
}
const vector<action_block> renderer::get_actions() {
return m_actions;
}
void renderer::debughints() {
#if DEBUG and DRAW_CLICKABLE_AREA_HINTS
map<alignment, int> hint_num{{
{alignment::LEFT, 0}, {alignment::CENTER, 0}, {alignment::RIGHT, 0},
}};
for (auto&& action : m_actions) {
if (action.active) {
continue;
}
m_log.info("Drawing clickable area hints");
hint_num[action.align]++;
auto x = action.start_x;
auto y = m_bar.y + hint_num[action.align]++ * DRAW_CLICKABLE_AREA_HINTS_OFFSET_Y;
auto w = action.end_x - action.start_x - 2;
auto h = m_bar.size.h - 2;
const uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT;
const uint32_t border_color = hint_num[action.align] % 2 ? 0xff0000 : 0x00ff00;
const uint32_t values[2]{border_color, true};
action.hint = m_connection.generate_id();
m_connection.create_window(m_screen->root_depth, action.hint, m_screen->root, x, y, w, h, 1,
XCB_WINDOW_CLASS_INPUT_OUTPUT, m_screen->root_visual, mask, values);
m_connection.map_window(action.hint);
}
m_connection.flush();
#endif
}
POLYBAR_NS_END

View file

@ -12,18 +12,17 @@ callback<bool> g_signals::bar::visibility_change{nullptr};
/** /**
* Signals used to communicate with the input parser * Signals used to communicate with the input parser
*/ */
callback<alignment> g_signals::parser::alignment_change{nullptr}; callback<const alignment> g_signals::parser::alignment_change{nullptr};
callback<attribute> g_signals::parser::attribute_set{nullptr}; callback<const attribute> g_signals::parser::attribute_set{nullptr};
callback<attribute> g_signals::parser::attribute_unset{nullptr}; callback<const attribute> g_signals::parser::attribute_unset{nullptr};
callback<attribute> g_signals::parser::attribute_toggle{nullptr}; callback<const mousebtn, string> g_signals::parser::action_block_open{nullptr};
callback<mousebtn, string> g_signals::parser::action_block_open{nullptr}; callback<const mousebtn> g_signals::parser::action_block_close{nullptr};
callback<mousebtn> g_signals::parser::action_block_close{nullptr}; callback<const gc, const uint32_t> g_signals::parser::color_change{nullptr};
callback<gc, color> g_signals::parser::color_change{nullptr}; callback<const int8_t> g_signals::parser::font_change{nullptr};
callback<int> g_signals::parser::font_change{nullptr}; callback<const int16_t> g_signals::parser::pixel_offset{nullptr};
callback<int> g_signals::parser::pixel_offset{nullptr}; callback<const uint16_t> g_signals::parser::ascii_text_write{nullptr};
callback<uint16_t> g_signals::parser::ascii_text_write{nullptr}; callback<const uint16_t> g_signals::parser::unicode_text_write{nullptr};
callback<uint16_t> g_signals::parser::unicode_text_write{nullptr}; callback<const char*, const size_t> g_signals::parser::string_write{nullptr};
callback<const char*, size_t> g_signals::parser::string_write{nullptr};
/** /**
* Signals used to communicate with the tray manager * Signals used to communicate with the tray manager

View file

@ -115,9 +115,9 @@ namespace drawtypes {
// avoid color bleed // avoid color bleed
if (icon_empty && icon_indicator) { if (icon_empty && icon_indicator) {
if (!icon_indicator->m_background.empty() && icon_empty->m_background.empty()) if (!icon_indicator->m_background.empty() && icon_empty->m_background.empty())
icon_empty->m_background = bar.background.source(); icon_empty->m_background = color_util::hex<uint16_t>(bar.background);
if (!icon_indicator->m_foreground.empty() && icon_empty->m_foreground.empty()) if (!icon_indicator->m_foreground.empty() && icon_empty->m_foreground.empty())
icon_empty->m_foreground = bar.foreground.source(); icon_empty->m_foreground = color_util::hex<uint16_t>(bar.foreground);
} }
progressbar->set_empty(move(icon_empty)); progressbar->set_empty(move(icon_empty));

View file

@ -41,7 +41,7 @@ color::operator XRenderColor() const {
} }
color::operator string() const { color::operator string() const {
return color_util::hex(m_color); return color_util::hex<uint8_t>(m_color);
} }
color::operator uint32_t() const { color::operator uint32_t() const {

View file

@ -37,21 +37,7 @@ font_manager::~font_manager() {
m_fonts.clear(); m_fonts.clear();
} }
void font_manager::set_preferred_font(int index) { bool font_manager::load(string name, int8_t fontindex, int8_t offset_y) {
if (index <= 0) {
m_fontindex = -1;
return;
}
for (auto&& font : m_fonts) {
if (font.first == index) {
m_fontindex = index;
break;
}
}
}
bool font_manager::load(string name, int fontindex, int offset_y) {
if (fontindex != -1 && m_fonts.find(fontindex) != m_fonts.end()) { if (fontindex != -1 && 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);
return false; return false;
@ -92,6 +78,20 @@ bool font_manager::load(string name, int fontindex, int offset_y) {
return true; return true;
} }
void font_manager::set_preferred_font(int8_t index) {
if (index <= 0) {
m_fontindex = -1;
return;
}
for (auto&& font : m_fonts) {
if (font.first == index) {
m_fontindex = index;
break;
}
}
}
font_t& font_manager::match_char(uint16_t chr) { font_t& font_manager::match_char(uint16_t chr) {
static font_t notfound; static font_t notfound;
if (!m_fonts.empty()) { if (!m_fonts.empty()) {
@ -108,7 +108,7 @@ font_t& font_manager::match_char(uint16_t chr) {
return notfound; return notfound;
} }
int font_manager::char_width(font_t& font, uint16_t chr) { uint8_t font_manager::char_width(font_t& font, uint16_t chr) {
if (!font) if (!font)
return 0; return 0;
@ -142,6 +142,28 @@ XftColor font_manager::xftcolor() {
return m_xftcolor; return m_xftcolor;
} }
XftDraw* font_manager::xftdraw() {
return m_xftdraw;
}
XftDraw* font_manager::create_xftdraw(xcb_pixmap_t pm, xcb_colormap_t cm) {
m_xftdraw = XftDrawCreate(xlib::get_display(), pm, xlib::get_visual(), cm);
return m_xftdraw;
}
void font_manager::destroy_xftdraw() {
XftDrawDestroy(m_xftdraw);
}
void font_manager::allocate_color(uint32_t color, bool initial_alloc) {
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);
allocate_color(x, initial_alloc);
}
void font_manager::allocate_color(XRenderColor color, bool initial_alloc) { void font_manager::allocate_color(XRenderColor color, bool initial_alloc) {
if (!initial_alloc) if (!initial_alloc)
XftColorFree(m_display, m_visual, m_colormap, &m_xftcolor); XftColorFree(m_display, m_visual, m_colormap, &m_xftcolor);
@ -150,7 +172,7 @@ void font_manager::allocate_color(XRenderColor color, bool initial_alloc) {
m_logger.err("Failed to allocate color"); m_logger.err("Failed to allocate color");
} }
void font_manager::set_gcontext_font(gcontext& gc, xcb_font_t font) { void font_manager::set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font) {
const uint32_t values[1]{font}; const uint32_t values[1]{font};
m_connection.change_gc(gc, XCB_GC_FONT, values); m_connection.change_gc(gc, XCB_GC_FONT, values);
} }