From 25e33b6aabb40acda6956f6dd566f65cd7120713 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Mon, 21 Nov 2016 15:07:00 +0100 Subject: [PATCH] refactor: Separate render component --- include/components/bar.hpp | 72 +-- include/components/parser.hpp | 11 +- include/components/renderer.hpp | 83 +++ include/components/signals.hpp | 40 +- include/components/types.hpp | 127 +++-- include/utils/color.hpp | 6 +- include/x11/fonts.hpp | 28 +- src/components/bar.cpp | 882 +++++++------------------------- src/components/builder.cpp | 6 +- src/components/controller.cpp | 28 +- src/components/parser.cpp | 42 +- src/components/renderer.cpp | 499 ++++++++++++++++++ src/components/signals.cpp | 23 +- src/drawtypes/progressbar.cpp | 4 +- src/x11/color.cpp | 2 +- src/x11/fonts.cpp | 56 +- 16 files changed, 985 insertions(+), 924 deletions(-) create mode 100644 include/components/renderer.hpp create mode 100644 src/components/renderer.cpp diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 7f7e1136..87dca42e 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -2,7 +2,6 @@ #include "common.hpp" #include "components/config.hpp" -#include "components/logger.hpp" #include "components/types.hpp" #include "utils/concurrency.hpp" #include "utils/throttle.hpp" @@ -15,11 +14,12 @@ POLYBAR_NS // fwd class tray_manager; -class font_manager; +class logger; +class renderer; class bar : public xpp::event::sink { public: - explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr font_manager, + explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr tray_manager); ~bar(); @@ -32,14 +32,9 @@ class bar : public xpp::event::sink m_fontmanager; unique_ptr m_tray; - - 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; + unique_ptr m_renderer; bar_settings m_opts; - map m_borders; - map m_gcontexts; - vector m_actions; + xcb_window_t m_window; + xcb_screen_t* m_screen; + 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}; - 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; + std::mutex m_mutex; }; di::injector> configure_bar(); diff --git a/include/components/parser.hpp b/include/components/parser.hpp index 959ad4a9..ba828069 100644 --- a/include/components/parser.hpp +++ b/include/components/parser.hpp @@ -1,22 +1,25 @@ #pragma once #include "common.hpp" -#include "components/signals.hpp" POLYBAR_NS +struct bar_settings; +enum class attribute : uint8_t; +enum class mousebtn : uint8_t; + DEFINE_ERROR(unrecognized_token); class parser { public: - explicit parser(const bar_settings& bar) : m_bar(bar) {} + explicit parser(const bar_settings& bar); void operator()(string data); void codeblock(string data); size_t text(string data); protected: - color parse_color(string s, color fallback = color{0}); - int parse_fontindex(string s); + uint32_t parse_color(string s, uint32_t fallback = 0); + int8_t parse_fontindex(string s); attribute parse_attr(const char s); mousebtn parse_action_btn(string data); string parse_action_cmd(string data); diff --git a/include/components/renderer.hpp b/include/components/renderer.hpp new file mode 100644 index 00000000..2c2d3f33 --- /dev/null +++ b/include/components/renderer.hpp @@ -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, + const bar_settings& bar, const vector& 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& 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 get_actions(); + + protected: + void debughints(); + + private: + connection& m_connection; + const logger& m_log; + unique_ptr 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 m_gcontexts; + map m_pixmaps; + vector 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> configure_renderer(const bar_settings& bar, const vector& fonts); + +POLYBAR_NS_END diff --git a/include/components/signals.hpp b/include/components/signals.hpp index 4e545b37..e02a9704 100644 --- a/include/components/signals.hpp +++ b/include/components/signals.hpp @@ -8,12 +8,11 @@ POLYBAR_NS // fwd decl {{{ -enum class mousebtn; -enum class syntaxtag; -enum class alignment; -enum class attribute; -enum class gc; -class color; +enum class mousebtn : uint8_t; +enum class syntaxtag : uint8_t; +enum class alignment : uint8_t; +enum class attribute : uint8_t; +enum class gc : uint8_t; // }}} @@ -23,27 +22,26 @@ class color; namespace g_signals { namespace bar { extern callback action_click; - extern callback visibility_change; + extern callback visibility_change; } namespace parser { - extern callback alignment_change; - extern callback attribute_set; - extern callback attribute_unset; - extern callback attribute_toggle; - extern callback action_block_open; - extern callback action_block_close; - extern callback color_change; - extern callback font_change; - extern callback pixel_offset; - extern callback ascii_text_write; - extern callback unicode_text_write; - extern callback string_write; + extern callback alignment_change; + extern callback attribute_set; + extern callback attribute_unset; + extern callback action_block_open; + extern callback action_block_close; + extern callback color_change; + extern callback font_change; + extern callback pixel_offset; + extern callback ascii_text_write; + extern callback unicode_text_write; + extern callback string_write; } namespace tray { - extern callback report_slotcount; - extern callback clear_bg; + extern callback report_slotcount; + extern callback clear_bg; } } diff --git a/include/components/types.hpp b/include/components/types.hpp index 1057f27c..3108a985 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -6,19 +6,13 @@ POLYBAR_NS -enum class border { NONE = 0, TOP, BOTTOM, LEFT, RIGHT, ALL }; -enum class alignment { NONE = 0, LEFT, CENTER, RIGHT }; -enum class syntaxtag { NONE = 0, A, B, F, T, U, O, R, o, u }; -enum class attribute { NONE = 0, o = 2, u = 4 }; -enum class mousebtn { NONE = 0, LEFT, MIDDLE, RIGHT, SCROLL_UP, SCROLL_DOWN }; -enum class gc { NONE = 0, BG, FG, OL, UL, BT, BB, BL, BR }; - -struct rect { - uint16_t w{0}; - uint16_t h{0}; -}; - -enum class strut { +enum class edge : uint8_t { NONE = 0, TOP, BOTTOM, LEFT, RIGHT, ALL }; +enum class alignment : uint8_t { NONE = 0, LEFT, CENTER, RIGHT }; +enum class syntaxtag : uint8_t { NONE = 0, A, B, F, T, U, O, R, o, u }; +enum class attribute : uint8_t { NONE = 0, o = 1 << 0, u = 1 << 1 }; +enum class mousebtn : uint8_t { NONE = 0, LEFT, MIDDLE, RIGHT, SCROLL_UP, SCROLL_DOWN }; +enum class gc : uint8_t { NONE = 0, BG, FG, OL, UL, BT, BB, BL, BR }; +enum class strut : uint16_t { LEFT = 0, RIGHT, TOP, @@ -33,80 +27,73 @@ enum class strut { BOTTOM_END_X, }; -struct strut_margins { - uint16_t t; - uint16_t b; - uint16_t l; - uint16_t r; -}; - -struct bar_settings { - bar_settings() = default; - - string locale; - +struct position { int16_t x{0}; int16_t y{0}; - uint16_t width{0}; - uint16_t height{0}; +}; - int16_t offset_y{0}; - int16_t offset_x{0}; +struct size { + uint16_t w{0}; + uint16_t h{0}; +}; - uint16_t padding_left{0}; - uint16_t padding_right{0}; +struct side_values { + uint16_t left{0}; + uint16_t right{0}; +}; - int16_t module_margin_left{0}; - int16_t module_margin_right{2}; - - int16_t lineheight{0}; - int16_t spacing{1}; - 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 edge_values { + uint16_t left{0}; + uint16_t right{0}; + uint16_t top{0}; + uint16_t bottom{0}; }; struct border_settings { - border_settings() = default; - polybar::color color{g_colorblack}; + uint32_t color{0xFF000000}; 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 borders; + + int8_t lineheight{0}; + int8_t spacing{1}; + string separator; + + string wmname; + string locale; + + bool force_docking{false}; +}; + struct action_block { - action_block() = default; - mousebtn button{mousebtn::NONE}; - string command; + alignment align{alignment::NONE}; int16_t start_x{0}; int16_t end_x{0}; - alignment align; + mousebtn button{mousebtn::NONE}; + string command; bool active{true}; #if DEBUG and DRAW_CLICKABLE_AREA_HINTS - xcb_window_t clickable_area; + xcb_window_t hint; #endif }; -struct wmsettings_bspwm {}; - POLYBAR_NS_END diff --git a/include/utils/color.hpp b/include/utils/color.hpp index ed0756d3..1598ddaf 100644 --- a/include/utils/color.hpp +++ b/include/utils/color.hpp @@ -54,7 +54,7 @@ namespace color_util { return (a << 24) | (r << 16) | (g << 8) | b; } - template + template string hex(uint32_t color) { char s[12]; size_t len = 0; @@ -85,9 +85,9 @@ namespace color_util { return hex; } - inline uint32_t parse(string hex) { + inline uint32_t parse(string hex, uint32_t fallback = 0) { if ((hex = parse_hex(hex)).empty()) - return 0U; + return fallback; return std::strtoul(&hex[1], nullptr, 16); } } diff --git a/include/x11/fonts.hpp b/include/x11/fonts.hpp index d163f1d3..fb1249e9 100644 --- a/include/x11/fonts.hpp +++ b/include/x11/fonts.hpp @@ -42,36 +42,40 @@ class font_manager { explicit font_manager(connection& conn, const logger& logger); ~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); - - int char_width(font_t& font, uint16_t chr); + uint8_t char_width(font_t& font, uint16_t chr); 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 set_gcontext_font(gcontext& gc, xcb_font_t font); + void set_gcontext_font(xcb_gcontext_t gc, xcb_font_t font); protected: bool open_xcb_font(font_t& fontptr, string fontname); - bool has_glyph(font_t& font, uint16_t chr); private: connection& m_connection; const logger& m_logger; - Display* m_display = nullptr; - Visual* m_visual = nullptr; - Colormap m_colormap; + Display* m_display{nullptr}; + Visual* m_visual{nullptr}; + Colormap m_colormap{}; - map m_fonts; - int m_fontindex = -1; - XftColor m_xftcolor; + map m_fonts; + int8_t m_fontindex{-1}; + + XftColor m_xftcolor{}; + XftDraw* m_xftdraw{nullptr}; }; di::injector> configure_font_manager(); diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 3bda29dc..e092083d 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -2,6 +2,7 @@ #include "components/bar.hpp" #include "components/parser.hpp" +#include "components/renderer.hpp" #include "components/signals.hpp" #include "utils/bspwm.hpp" #include "utils/color.hpp" @@ -31,7 +32,6 @@ di::injector> configure_bar() { configure_connection(), configure_config(), configure_logger(), - configure_font_manager(), configure_tray_manager()); // clang-format on } @@ -39,25 +39,19 @@ di::injector> configure_bar() { /** * Construct bar instance */ -bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr font_manager, - unique_ptr tray_manager) - : m_connection(conn) - , m_conf(config) - , m_log(logger) - , m_fontmanager(forward(font_manager)) - , m_tray(forward(tray_manager)) {} +bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr tray_manager) + : m_connection(conn), m_conf(config), m_log(logger), m_tray(forward(tray_manager)) {} /** * Cleanup signal handlers and destroy the bar window */ bar::~bar() { - std::lock_guard lck(m_lock); + std::lock_guard guard(m_mutex); // Disconnect signal handlers {{{ g_signals::parser::alignment_change = nullptr; g_signals::parser::attribute_set = nullptr; g_signals::parser::attribute_unset = nullptr; - g_signals::parser::attribute_toggle = nullptr; g_signals::parser::action_block_open = nullptr; g_signals::parser::action_block_close = nullptr; g_signals::parser::color_change = nullptr; @@ -75,9 +69,6 @@ bar::~bar() { if (m_sinkattached) { m_connection.detach_sink(this, 1); } - - m_connection.destroy_window(m_window); - m_connection.flush(); } /** @@ -94,63 +85,63 @@ void bar::bootstrap(bool nodraw) { m_screensize.w = geom->width; m_screensize.h = geom->height; - // limit the amount of allowed input events to 1 per 60ms - m_throttler = throttle_util::make_throttler(1, chrono::milliseconds{60}); - - m_opts.separator = string_util::trim(m_conf.get(bs, "separator", ""), '"'); m_opts.locale = m_conf.get(bs, "locale", ""); + m_opts.separator = string_util::trim(m_conf.get(bs, "separator", ""), '"'); create_monitor(); // Set bar colors {{{ - m_opts.background = color::parse(m_conf.get(bs, "background", m_opts.background.source())); - m_opts.foreground = color::parse(m_conf.get(bs, "foreground", m_opts.foreground.source())); - m_opts.linecolor = color::parse(m_conf.get(bs, "linecolor", m_opts.linecolor.source())); + m_opts.background = color::parse(m_conf.get(bs, "background", color_util::hex(m_opts.background))); + m_opts.foreground = color::parse(m_conf.get(bs, "foreground", color_util::hex(m_opts.foreground))); + m_opts.linecolor = color::parse(m_conf.get(bs, "linecolor", color_util::hex(m_opts.linecolor))); // }}} // Set border values {{{ auto bsize = m_conf.get(bs, "border-size", 0); - auto bcolor = m_conf.get(bs, "border-color", g_colorempty.source()); + auto bcolor = m_conf.get(bs, "border-color", "#00000000"); - m_borders.emplace(border::TOP, border_settings{}); - m_borders[border::TOP].size = m_conf.get(bs, "border-top", bsize); - m_borders[border::TOP].color = color::parse(m_conf.get(bs, "border-top-color", bcolor)); + m_opts.borders.emplace(edge::TOP, border_settings{}); + m_opts.borders[edge::TOP].size = m_conf.get(bs, "border-top", bsize); + m_opts.borders[edge::TOP].color = color::parse(m_conf.get(bs, "border-top-color", bcolor)); - m_borders.emplace(border::BOTTOM, border_settings{}); - m_borders[border::BOTTOM].size = m_conf.get(bs, "border-bottom", bsize); - m_borders[border::BOTTOM].color = color::parse(m_conf.get(bs, "border-bottom-color", bcolor)); + m_opts.borders.emplace(edge::BOTTOM, border_settings{}); + m_opts.borders[edge::BOTTOM].size = m_conf.get(bs, "border-bottom", bsize); + m_opts.borders[edge::BOTTOM].color = color::parse(m_conf.get(bs, "border-bottom-color", bcolor)); - m_borders.emplace(border::LEFT, border_settings{}); - m_borders[border::LEFT].size = m_conf.get(bs, "border-left", bsize); - m_borders[border::LEFT].color = color::parse(m_conf.get(bs, "border-left-color", bcolor)); + m_opts.borders.emplace(edge::LEFT, border_settings{}); + m_opts.borders[edge::LEFT].size = m_conf.get(bs, "border-left", bsize); + m_opts.borders[edge::LEFT].color = color::parse(m_conf.get(bs, "border-left-color", bcolor)); - m_borders.emplace(border::RIGHT, border_settings{}); - m_borders[border::RIGHT].size = m_conf.get(bs, "border-right", bsize); - m_borders[border::RIGHT].color = color::parse(m_conf.get(bs, "border-right-color", bcolor)); + m_opts.borders.emplace(edge::RIGHT, border_settings{}); + m_opts.borders[edge::RIGHT].size = m_conf.get(bs, "border-right", bsize); + m_opts.borders[edge::RIGHT].color = color::parse(m_conf.get(bs, "border-right-color", bcolor)); // }}} // Set size and position {{{ - GET_CONFIG_VALUE(bs, m_opts.dock, "dock"); - GET_CONFIG_VALUE(bs, m_opts.bottom, "bottom"); + if (m_conf.get(bs, "bottom", false)) + m_opts.origin = edge::BOTTOM; + else + m_opts.origin = edge::TOP; + + GET_CONFIG_VALUE(bs, m_opts.force_docking, "dock"); GET_CONFIG_VALUE(bs, m_opts.spacing, "spacing"); GET_CONFIG_VALUE(bs, m_opts.lineheight, "lineheight"); - GET_CONFIG_VALUE(bs, m_opts.padding_left, "padding-left"); - GET_CONFIG_VALUE(bs, m_opts.padding_right, "padding-right"); - GET_CONFIG_VALUE(bs, m_opts.module_margin_left, "module-margin-left"); - GET_CONFIG_VALUE(bs, m_opts.module_margin_right, "module-margin-right"); + GET_CONFIG_VALUE(bs, m_opts.padding.left, "padding-left"); + GET_CONFIG_VALUE(bs, m_opts.padding.right, "padding-right"); + GET_CONFIG_VALUE(bs, m_opts.module_margin.left, "module-margin-left"); + GET_CONFIG_VALUE(bs, m_opts.module_margin.right, "module-margin-right"); - m_opts.margins.t = m_conf.get("global/wm", "margin-top", 0); - m_opts.margins.b = m_conf.get("global/wm", "margin-bottom", 0); + m_opts.strut.top = m_conf.get("global/wm", "margin-top", 0); + m_opts.strut.bottom = m_conf.get("global/wm", "margin-bottom", 0); // }}} // Set the WM_NAME value {{{ // Required early for --print-wmname - m_opts.wmname = "polybar-" + bs.substr(4) + "_" + m_opts.monitor->name; - m_opts.wmname = m_conf.get(bs, "wm-name", m_opts.wmname); + m_opts.wmname = m_conf.get(bs, "wm-name", "polybar-" + bs.substr(4) + "_" + m_opts.monitor->name); m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-"); // }}} @@ -162,44 +153,45 @@ void bar::bootstrap(bool nodraw) { return; } - // }}} - // Setup graphic components and create window {{{ - - m_log.trace("bar: Get true color visual"); - m_visual = m_connection.visual_type(m_screen, 32).get(); - - m_log.trace("bar: Create colormap"); - m_colormap = colormap{m_connection, m_connection.generate_id()}; - m_connection.create_colormap(XCB_COLORMAP_ALLOC_NONE, m_colormap, m_screen->root, m_visual->visual_id); - - configure_geom(); - create_window(); - create_pixmap(); - create_gcontexts(); - restack_window(); - set_wmhints(); - map_window(); - - m_connection.flush(); - // }}} // Connect signal handlers and attach sink {{{ m_log.trace("bar: Attach parser callbacks"); // clang-format off - g_signals::parser::alignment_change = bind(&bar::on_alignment_change, this, placeholders::_1); - g_signals::parser::attribute_set = bind(&bar::on_attribute_set, this, placeholders::_1); - g_signals::parser::attribute_unset = bind(&bar::on_attribute_unset, this, placeholders::_1); - g_signals::parser::attribute_toggle = bind(&bar::on_attribute_toggle, this, placeholders::_1); - g_signals::parser::action_block_open = bind(&bar::on_action_block_open, this, placeholders::_1, placeholders::_2); - g_signals::parser::action_block_close = bind(&bar::on_action_block_close, this, placeholders::_1); - g_signals::parser::color_change = bind(&bar::on_color_change, this, placeholders::_1, placeholders::_2); - g_signals::parser::font_change = bind(&bar::on_font_change, this, placeholders::_1); - g_signals::parser::pixel_offset = bind(&bar::on_pixel_offset, this, placeholders::_1); - g_signals::parser::ascii_text_write = bind(&bar::draw_character, this, placeholders::_1); - g_signals::parser::unicode_text_write = bind(&bar::draw_character, this, placeholders::_1); - g_signals::parser::string_write = bind(&bar::draw_textstring, this, placeholders::_1, placeholders::_2); + g_signals::parser::alignment_change = [this](const alignment align) { + m_renderer->set_alignment(align); + }; + g_signals::parser::attribute_set = [this](const attribute attr) { + m_renderer->set_attribute(attr, true); + }; + g_signals::parser::attribute_unset = [this](const attribute attr) { + m_renderer->set_attribute(attr, false); + }; + g_signals::parser::action_block_open = [this](const mousebtn btn, string cmd) { + m_renderer->begin_action(btn, cmd); + }; + g_signals::parser::action_block_close = [this](const mousebtn btn) { + m_renderer->end_action(btn); + }; + g_signals::parser::color_change= [this](const gc gcontext, const uint32_t color) { + m_renderer->set_foreground(gcontext, color); + }; + g_signals::parser::font_change = [this](const int8_t font) { + m_renderer->set_fontindex(font); + }; + g_signals::parser::pixel_offset = [this](const int16_t px) { + m_renderer->shift_content(px); + }; + g_signals::parser::ascii_text_write = [this](const uint16_t c) { + m_renderer->draw_character(c); + }; + g_signals::parser::unicode_text_write = [this](const uint16_t c) { + m_renderer->draw_character(c); + }; + g_signals::parser::string_write = [this](const char* text, const size_t len) { + m_renderer->draw_textstring(text, len); + }; // clang-format on m_log.trace("bar: Attaching sink to registry"); @@ -207,12 +199,18 @@ void bar::bootstrap(bool nodraw) { m_sinkattached = true; // }}} - // Load fonts {{{ - m_log.trace("bar: Load fonts"); - load_fonts(); + configure_geom(); + + m_renderer = configure_renderer(m_opts, m_conf.get_list(bs, "font", {})).create>(); + m_window = m_renderer->window(); + + restack_window(); + set_wmhints(); + map_window(); + + m_connection.flush(); - // }}} } /** @@ -243,11 +241,11 @@ void bar::bootstrap_tray() { return; } - m_traypos = settings.align; + m_trayalign = settings.align; - settings.height = m_opts.height; - settings.height -= m_borders.at(border::BOTTOM).size; - settings.height -= m_borders.at(border::TOP).size; + settings.height = m_opts.size.h; + settings.height -= m_opts.borders.at(edge::BOTTOM).size; + settings.height -= m_opts.borders.at(edge::TOP).size; settings.height_fill = settings.height; if (settings.height % 2 != 0) { @@ -260,9 +258,9 @@ void bar::bootstrap_tray() { settings.height = maxsize; } - settings.width_max = m_opts.width; + settings.width_max = m_opts.size.w; settings.width = settings.height; - settings.orig_y = m_opts.y + m_borders.at(border::TOP).size; + settings.orig_y = m_opts.pos.y + m_opts.borders.at(edge::TOP).size; // Apply user-defined scaling auto scale = m_conf.get(bs, "tray-scale", 1.0); @@ -270,9 +268,9 @@ void bar::bootstrap_tray() { settings.height_fill *= scale; if (settings.align == alignment::RIGHT) { - settings.orig_x = m_opts.x + m_opts.width - m_borders.at(border::RIGHT).size; + settings.orig_x = m_opts.pos.x + m_opts.size.w - m_opts.borders.at(edge::RIGHT).size; } else if (settings.align == alignment::LEFT) { - settings.orig_x = m_opts.x + m_borders.at(border::LEFT).size; + settings.orig_x = m_opts.pos.x + m_opts.borders.at(edge::LEFT).size; } else if (settings.align == alignment::CENTER) { settings.orig_x = get_centerx() - (settings.width / 2); } @@ -371,96 +369,34 @@ const bar_settings bar::settings() const { * @param force Unless true, do not parse unchanged data */ void bar::parse(string data, bool force) { - std::lock_guard lck(m_lock); - { - if (data == m_prevdata && !force) - return; - - m_prevdata = data; - - // TODO: move to font_manager - m_xftdraw = XftDrawCreate(xlib::get_display(), m_pixmap, xlib::get_visual(), m_colormap); - - m_opts.align = alignment::LEFT; - m_xpos = m_borders[border::LEFT].size; - m_attributes = 0; - -#if DEBUG and DRAW_CLICKABLE_AREA_HINTS - for (auto&& action : m_actions) { - m_connection.destroy_window(action.clickable_area); - } -#endif - - m_actions.clear(); - - draw_background(); - - if (m_traypos == alignment::LEFT && m_trayclients > 0) { - auto& tray = m_tray->settings(); - m_xpos += ((tray.width + tray.spacing) * m_trayclients) + tray.spacing; - } - - try { - parser parser(m_opts); - parser(data); - } catch (const unrecognized_token& err) { - m_log.err("Unrecognized syntax token '%s'", err.what()); - } - - if (m_traypos == alignment::RIGHT && m_trayclients > 0) { - auto& tray = m_tray->settings(); - draw_shift(m_xpos, ((tray.width + tray.spacing) * m_trayclients) + tray.spacing); - } - - draw_border(border::ALL); - - XftDrawDestroy(m_xftdraw); - - flush(); + if (!m_mutex.try_lock()) { + return; } -} -/** - * Copy the contents of the pixmap's onto the bar window - */ -void bar::flush() { - m_connection.copy_area(m_pixmap, m_window, m_gcontexts.at(gc::FG), 0, 0, 0, 0, m_opts.width, m_opts.height); - m_connection.flush(); + std::lock_guard guard(m_mutex, std::adopt_lock); -#if DEBUG and DRAW_CLICKABLE_AREA_HINTS - map hint_num{{ - {alignment::LEFT, 0}, {alignment::CENTER, 0}, {alignment::RIGHT, 0}, - }}; -#endif + if (data == m_lastinput && !force) + return; - for (auto&& action : m_actions) { - if (action.active) { - m_log.warn("Action block not closed"); - m_log.warn("action.command = %s", action.command); - } else { - m_log.trace_x("bar: Action details (button = %i, start_x = %i, end_x = %i, command = '%s')", - static_cast(action.button), action.start_x, action.end_x, action.command); -#if DEBUG and DRAW_CLICKABLE_AREA_HINTS - m_log.info("Drawing clickable area hints"); + m_lastinput = data; - hint_num[action.align]++; - - auto x = action.start_x; - auto y = m_opts.y + hint_num[action.align]++ * DRAW_CLICKABLE_AREA_HINTS_OFFSET_Y; - auto w = action.end_x - action.start_x - 2; - auto h = m_opts.height - 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.clickable_area = window{m_connection, m_connection.generate_id()}; - m_connection.create_window_checked(m_screen->root_depth, action.clickable_area, m_screen->root, x, y, w, h, 1, - XCB_WINDOW_CLASS_INPUT_OUTPUT, m_screen->root_visual, mask, values); - m_connection.map_window_checked(action.clickable_area); -#endif - } + if (m_trayclients) { + if (m_tray && m_trayalign == alignment::LEFT) + m_renderer->reserve_space(edge::LEFT, m_tray->settings().configured_w); + else if (m_tray && m_trayalign == alignment::RIGHT) + m_renderer->reserve_space(edge::RIGHT, m_tray->settings().configured_w); } + + m_renderer->begin(); + + try { + parser parser(m_opts); + parser(data); + } catch (const unrecognized_token& err) { + m_log.err("Unrecognized syntax token '%s'", err.what()); + } + + m_renderer->end(); } /** @@ -470,44 +406,6 @@ void bar::refresh_window() { m_log.info("Refresh bar window"); } -/** - * Load user-defined fonts - */ -void bar::load_fonts() { - auto fonts_loaded = false; - auto fontindex = 0; - auto fonts = m_conf.get_list(m_conf.bar_section(), "font", {}); - - if (fonts.empty()) { - m_log.warn("No fonts specified, using fallback font \"fixed\""); - } - - for (auto f : fonts) { - fontindex++; - vector 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_opts.foreground, true); -} - /** * Configure geometry values */ @@ -521,47 +419,47 @@ void bar::configure_geom() { auto offsety = m_conf.get(m_conf.bar_section(), "offset-y", ""); // look for user-defined width - if ((m_opts.width = atoi(w.c_str())) && w.find("%") != string::npos) { - m_opts.width = math_util::percentage_to_value(m_opts.width, m_opts.monitor->w); + if ((m_opts.size.w = atoi(w.c_str())) && w.find("%") != string::npos) { + m_opts.size.w = math_util::percentage_to_value(m_opts.size.w, m_opts.monitor->w); } // look for user-defined height - if ((m_opts.height = atoi(h.c_str())) && h.find("%") != string::npos) { - m_opts.height = math_util::percentage_to_value(m_opts.height, m_opts.monitor->h); + if ((m_opts.size.h = atoi(h.c_str())) && h.find("%") != string::npos) { + m_opts.size.h = math_util::percentage_to_value(m_opts.size.h, m_opts.monitor->h); } // look for user-defined offset-x - if ((m_opts.offset_x = atoi(offsetx.c_str())) != 0 && offsetx.find("%") != string::npos) { - m_opts.offset_x = math_util::percentage_to_value(m_opts.offset_x, m_opts.monitor->w); + if ((m_opts.offset.x = atoi(offsetx.c_str())) != 0 && offsetx.find("%") != string::npos) { + m_opts.offset.x = math_util::percentage_to_value(m_opts.offset.x, m_opts.monitor->w); } // look for user-defined offset-y - if ((m_opts.offset_y = atoi(offsety.c_str())) != 0 && offsety.find("%") != string::npos) { - m_opts.offset_y = math_util::percentage_to_value(m_opts.offset_y, m_opts.monitor->h); + if ((m_opts.offset.y = atoi(offsety.c_str())) != 0 && offsety.find("%") != string::npos) { + m_opts.offset.y = math_util::percentage_to_value(m_opts.offset.y, m_opts.monitor->h); } // apply offsets - m_opts.x = m_opts.offset_x + m_opts.monitor->x; - m_opts.y = m_opts.offset_y + m_opts.monitor->y; + m_opts.pos.x = m_opts.offset.x + m_opts.monitor->x; + m_opts.pos.y = m_opts.offset.y + m_opts.monitor->y; // apply borders - m_opts.height += m_borders[border::TOP].size; - m_opts.height += m_borders[border::BOTTOM].size; + m_opts.size.h += m_opts.borders[edge::TOP].size; + m_opts.size.h += m_opts.borders[edge::BOTTOM].size; - if (m_opts.bottom) - m_opts.y = m_opts.monitor->y + m_opts.monitor->h - m_opts.height - m_opts.offset_y; + if (m_opts.origin == edge::BOTTOM) + m_opts.pos.y = m_opts.monitor->y + m_opts.monitor->h - m_opts.size.h - m_opts.offset.y; - if (m_opts.width <= 0 || m_opts.width > m_opts.monitor->w) + if (m_opts.size.w <= 0 || m_opts.size.w > m_opts.monitor->w) throw application_error("Resulting bar width is out of bounds"); - if (m_opts.height <= 0 || m_opts.height > m_opts.monitor->h) + if (m_opts.size.h <= 0 || m_opts.size.h > m_opts.monitor->h) throw application_error("Resulting bar height is out of bounds"); - m_opts.width = math_util::cap(m_opts.width, 0, m_opts.monitor->w); - m_opts.height = math_util::cap(m_opts.height, 0, m_opts.monitor->h); + m_opts.size.w = math_util::cap(m_opts.size.w, 0, m_opts.monitor->w); + m_opts.size.h = math_util::cap(m_opts.size.h, 0, m_opts.monitor->h); - m_opts.vertical_mid = (m_opts.height + m_borders[border::TOP].size - m_borders[border::BOTTOM].size) / 2; + m_opts.center.y = (m_opts.size.h + m_opts.borders[edge::TOP].size - m_opts.borders[edge::BOTTOM].size) / 2; - m_log.info("Bar geometry %ix%i+%i+%i", m_opts.width, m_opts.height, m_opts.x, m_opts.y); + m_log.info("Bar geometry %ix%i+%i+%i", m_opts.size.w, m_opts.size.h, m_opts.pos.x, m_opts.pos.y); } /** @@ -599,71 +497,6 @@ void bar::create_monitor() { m_log.trace("bar: Loaded monitor %s (%ix%i+%i+%i)", m->name, m->w, m->h, m->x, m->y); } -/** - * Create window object - */ -void bar::create_window() { - 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, ¶ms, back_pixel, 0); - XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, 0); - XCB_AUX_ADD_PARAM(&mask, ¶ms, backing_store, XCB_BACKING_STORE_WHEN_MAPPED); - XCB_AUX_ADD_PARAM(&mask, ¶ms, colormap, m_colormap); - XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, m_opts.dock); - XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS); - // clang-format on - - xutils::pack_values(mask, ¶ms, values); - m_connection.create_window_checked(32, m_window, m_screen->root, m_opts.x, m_opts.y, m_opts.width, m_opts.height, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, m_visual->visual_id, mask, values); -} - -/** - * Create window pixmap - */ -void bar::create_pixmap() { - m_log.trace("bar: Create pixmap (xid=%s)", m_connection.id(m_pixmap)); - m_connection.create_pixmap(32, m_pixmap, m_window, m_opts.width, m_opts.height); -} - -/** - * Create window gcontexts - */ -void bar::create_gcontexts() { - // clang-format off - vector colors { - m_opts.background, - m_opts.foreground, - m_opts.linecolor, - m_opts.linecolor, - m_borders[border::TOP].color, - m_borders[border::BOTTOM].color, - m_borders[border::LEFT].color, - m_borders[border::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, ¶ms, foreground, colors[i - 1]); - XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 0); - - xutils::pack_values(mask, ¶ms, value_list); - m_gcontexts.emplace(gc(i), gcontext{m_connection, 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); - } -} - /** * Move the bar window above defined sibling * in the X window stack @@ -682,9 +515,9 @@ void bar::restack_window() { if (wm_restack == "bspwm") { restacked = bspwm_util::restack_above_root(m_connection, m_opts.monitor, m_window); #if ENABLE_I3 - } else if (wm_restack == "i3" && m_opts.dock) { + } else if (wm_restack == "i3" && m_opts.force_docking) { restacked = i3_util::restack_above_root(m_connection, m_opts.monitor, m_window); - } else if (wm_restack == "i3" && !m_opts.dock) { + } else if (wm_restack == "i3" && !m_opts.force_docking) { m_log.warn("Ignoring restack of i3 window (not needed when dock = false)"); wm_restack.clear(); #endif @@ -705,26 +538,27 @@ void bar::restack_window() { */ void bar::map_window() { auto geom = m_connection.get_geometry(m_screen->root); - auto w = m_opts.width + m_opts.offset_x; - auto h = m_opts.height + m_opts.offset_y; - auto x = m_opts.x; - auto y = m_opts.y; + auto w = m_opts.size.w + m_opts.offset.x; + auto h = m_opts.size.h + m_opts.offset.y; + auto x = m_opts.pos.x; + auto y = m_opts.pos.y; - if (m_opts.bottom) { - h += m_opts.margins.t; + if (m_opts.origin == edge::BOTTOM) { + h += m_opts.strut.top; } else { - h += m_opts.margins.b; + h += m_opts.strut.bottom; } - if (m_opts.bottom && m_opts.monitor->y + m_opts.monitor->h < m_screensize.h) { + if (m_opts.origin == edge::BOTTOM && m_opts.monitor->y + m_opts.monitor->h < m_screensize.h) { h += m_screensize.h - (m_opts.monitor->y + m_opts.monitor->h); - } else if (!m_opts.bottom) { + } else if (m_opts.origin != edge::BOTTOM) { h += m_opts.monitor->y; } - m_window.map_checked(); - m_window.reconfigure_struts(w, h, x, m_opts.bottom); - m_window.reconfigure_pos(x, y); + window win{m_connection, m_window}; + win.map_checked(); + win.reconfigure_struts(w, h, x, m_opts.origin == edge::BOTTOM); + win.reconfigure_pos(x, y); } /** @@ -732,13 +566,13 @@ void bar::map_window() { */ void bar::set_wmhints() { m_log.trace("bar: Set WM_NAME"); - xcb_icccm_set_wm_name(m_connection, m_window, XCB_ATOM_STRING, 8, m_opts.wmname.length(), m_opts.wmname.c_str()); + xcb_icccm_set_wm_name(m_connection, m_window, XCB_ATOM_STRING, 8, m_opts.wmname.size(), m_opts.wmname.c_str()); xcb_icccm_set_wm_class(m_connection, m_window, 15, "polybar\0Polybar"); m_log.trace("bar: Set WM_NORMAL_HINTS"); xcb_size_hints_t hints; - xcb_icccm_size_hints_set_position(&hints, true, m_opts.x, m_opts.y); - xcb_icccm_size_hints_set_size(&hints, true, m_opts.width, m_opts.height); + xcb_icccm_size_hints_set_position(&hints, true, m_opts.pos.x, m_opts.pos.y); + xcb_icccm_size_hints_set_size(&hints, true, m_opts.size.w, m_opts.size.h); xcb_icccm_set_wm_normal_hints(m_connection, m_window, &hints); m_log.trace("bar: Set _NET_WM_WINDOW_TYPE"); @@ -758,10 +592,10 @@ void bar::set_wmhints() { * Get the horizontal center pos */ int bar::get_centerx() { - int x = m_opts.x; - x += m_opts.width; - x -= m_borders[border::RIGHT].size; - x += m_borders[border::LEFT].size; + int x = m_opts.pos.x; + x += m_opts.size.w; + x -= m_opts.borders[edge::RIGHT].size; + x += m_opts.borders[edge::LEFT].size; x /= 2; return x; } @@ -770,9 +604,9 @@ int bar::get_centerx() { * Get the inner width of the bar */ int bar::get_innerwidth() { - auto w = m_opts.width; - w -= m_borders[border::RIGHT].size; - w -= m_borders[border::LEFT].size; + auto w = m_opts.size.w; + w -= m_opts.borders[edge::RIGHT].size; + w -= m_opts.borders[edge::LEFT].size; return w; } @@ -782,48 +616,42 @@ int bar::get_innerwidth() { * Used to map mouse clicks to bar actions */ void bar::handle(const evt::button_press& evt) { - if (!m_throttler->passthrough(throttle_util::strategy::try_once_or_leave_yolo{})) { + std::lock_guard guard(m_mutex, std::adopt_lock); + + m_log.trace_x("bar: Received button press: %i at pos(%i, %i)", evt->detail, evt->event_x, evt->event_y); + + mousebtn button = static_cast(evt->detail); + + for (auto&& action : m_renderer->get_actions()) { + if (action.active) { + m_log.trace_x("bar: Ignoring action: unclosed)"); + continue; + } else if (action.button != button) { + m_log.trace_x("bar: Ignoring action: button mismatch"); + continue; + } else if (action.start_x > evt->event_x) { + m_log.trace_x("bar: Ignoring action: start_x(%i) > event_x(%i)", action.start_x, evt->event_x); + continue; + } else if (action.end_x < evt->event_x) { + m_log.trace_x("bar: Ignoring action: end_x(%i) < event_x(%i)", action.end_x, evt->event_x); + continue; + } + + m_log.trace("Found matching input area"); + m_log.trace_x("action.command = %s", action.command); + m_log.trace_x("action.button = %i", static_cast(action.button)); + m_log.trace_x("action.start_x = %i", action.start_x); + m_log.trace_x("action.end_x = %i", action.end_x); + + if (g_signals::bar::action_click) + g_signals::bar::action_click(action.command); + else + m_log.warn("No signal handler's connected to 'action_click'"); + return; } - std::lock_guard lck(m_lock); - { - m_log.trace_x("bar: Received button press event: %i at pos(%i, %i)", static_cast(evt->detail), evt->event_x, - evt->event_y); - - mousebtn button = static_cast(evt->detail); - - for (auto&& action : m_actions) { - if (action.active) { - m_log.trace_x("bar: Ignoring action: unclosed)"); - continue; - } else if (action.button != button) { - m_log.trace_x("bar: Ignoring action: button mismatch"); - continue; - } else if (action.start_x > evt->event_x) { - m_log.trace_x("bar: Ignoring action: start_x(%i) > event_x(%i)", action.start_x, evt->event_x); - continue; - } else if (action.end_x < evt->event_x) { - m_log.trace_x("bar: Ignoring action: end_x(%i) < event_x(%i)", action.end_x, evt->event_x); - continue; - } - - m_log.trace("Found matching input area"); - m_log.trace_x("action.command = %s", action.command); - m_log.trace_x("action.button = %i", static_cast(action.button)); - m_log.trace_x("action.start_x = %i", action.start_x); - m_log.trace_x("action.end_x = %i", action.end_x); - - if (g_signals::bar::action_click) - g_signals::bar::action_click(action.command); - else - m_log.warn("No signal handler's connected to 'action_click'"); - - return; - } - - m_log.warn("No matching input area found"); - } + m_log.warn("No matching input area found"); } /** @@ -834,7 +662,7 @@ void bar::handle(const evt::button_press& evt) { void bar::handle(const evt::expose& evt) { if (evt->window == m_window) { m_log.trace("bar: Received expose event"); - flush(); + m_renderer->redraw(); } } @@ -851,6 +679,7 @@ void bar::handle(const evt::expose& evt) { * pseudo-transparent background when it changes */ void bar::handle(const evt::property_notify& evt) { + (void)evt; #if DEBUG string atom_name = m_connection.get_atom_name(evt->atom).name(); m_log.trace("bar: property_notify(%s)", atom_name); @@ -883,141 +712,6 @@ void bar::handle(const evt::property_notify& evt) { } } -/** - * Handle alignment update - */ -void bar::on_alignment_change(alignment align) { - if (align == m_opts.align) - return; - - m_log.trace_x("bar: alignment_change(%i)", static_cast(align)); - m_opts.align = align; - - if (align == alignment::LEFT) { - m_xpos = m_borders[border::LEFT].size; - } else if (align == alignment::RIGHT) { - m_xpos = m_borders[border::RIGHT].size; - } else { - m_xpos = 0; - } -} - -/** - * Handle attribute on state - */ -void bar::on_attribute_set(attribute attr) { - int val{static_cast(attr)}; - if ((m_attributes & val) != 0) - return; - m_log.trace_x("bar: attribute_set(%i)", val); - m_attributes |= val; -} - -/** - * Handle attribute off state - */ -void bar::on_attribute_unset(attribute attr) { - int val{static_cast(attr)}; - if ((m_attributes & val) == 0) - return; - m_log.trace_x("bar: attribute_unset(%i)", val); - m_attributes ^= val; -} - -/** - * Handle attribute toggle state - */ -void bar::on_attribute_toggle(attribute attr) { - int val{static_cast(attr)}; - m_log.trace_x("bar: attribute_toggle(%i)", val); - m_attributes ^= val; -} - -/** - * Handle action block start - */ -void bar::on_action_block_open(mousebtn btn, string cmd) { - if (btn == mousebtn::NONE) - btn = mousebtn::LEFT; - m_log.trace_x("bar: action_block_open(%i, %s)", static_cast(btn), cmd); - action_block action; - action.active = true; - action.align = m_opts.align; - action.button = btn; - action.start_x = m_xpos; - action.command = string_util::replace_all(cmd, ":", "\\:"); - m_actions.emplace_back(action); -} - -/** - * Handle action block end - */ -void bar::on_action_block_close(mousebtn btn) { - m_log.trace_x("bar: action_block_close(%i)", static_cast(btn)); - - for (auto i = m_actions.size(); i > 0; i--) { - auto& action = m_actions[i - 1]; - - if (!action.active || action.button != btn) - continue; - - action.active = false; - - if (action.align == alignment::LEFT) { - action.end_x = m_xpos; - } else if (action.align == alignment::CENTER) { - int base_x = m_opts.width; - base_x -= m_borders[border::RIGHT].size; - base_x /= 2; - base_x += m_borders[border::LEFT].size; - - int clickable_width = m_xpos - action.start_x; - 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_opts.width - m_borders[border::RIGHT].size; - action.start_x = base_x - m_xpos + action.start_x; - action.end_x = base_x; - } - - return; - } -} - -/** - * Handle color change - */ -void bar::on_color_change(gc gc_, color color_) { - m_log.trace_x( - "bar: color_change(%i, %s -> #%07x)", static_cast(gc_), color_.source(), static_cast(color_)); - - const uint32_t value_list[]{color_}; - m_connection.change_gc(m_gcontexts.at(gc_), XCB_GC_FOREGROUND, value_list); - - if (gc_ == gc::FG) { - m_fontmanager->allocate_color(color_); - } else if (gc_ == gc::BG) { - draw_shift(m_xpos, 0); - } -} - -/** - * Handle font change - */ -void bar::on_font_change(int index) { - m_log.trace_x("bar: font_change(%i)", index); - m_fontmanager->set_preferred_font(index); -} - -/** - * Handle pixel offsetting - */ -void bar::on_pixel_offset(int px) { - m_log.trace_x("bar: pixel_offset(%i)", px); - draw_shift(m_xpos, px); - m_xpos += px; -} - /** * Proess systray report */ @@ -1029,207 +723,9 @@ void bar::on_tray_report(uint16_t slots) { m_log.trace("bar: tray_report(%lu)", slots); m_trayclients = slots; - if (!m_prevdata.empty()) { - parse(m_prevdata, true); + if (!m_lastinput.empty()) { + parse(m_lastinput, true); } } -/** - * Draw background onto the pixmap - */ -void bar::draw_background() { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), 0, 0, m_opts.width, m_opts.height); -} - -/** - * Draw borders onto the pixmap - */ -void bar::draw_border(border border_) { - switch (border_) { - case border::NONE: - break; - - case border::TOP: - if (m_borders[border::TOP].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BT), m_borders[border::LEFT].size, 0, - m_opts.width - m_borders[border::LEFT].size - m_borders[border::RIGHT].size, m_borders[border::TOP].size); - } - break; - - case border::BOTTOM: - if (m_borders[border::BOTTOM].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BB), m_borders[border::LEFT].size, - m_opts.height - m_borders[border::BOTTOM].size, - m_opts.width - m_borders[border::LEFT].size - m_borders[border::RIGHT].size, - m_borders[border::BOTTOM].size); - } - break; - - case border::LEFT: - if (m_borders[border::LEFT].size > 0) { - draw_util::fill( - m_connection, m_pixmap, m_gcontexts.at(gc::BL), 0, 0, m_borders[border::LEFT].size, m_opts.height); - } - break; - - case border::RIGHT: - if (m_borders[border::RIGHT].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BR), m_opts.width - m_borders[border::RIGHT].size, 0, - m_borders[border::RIGHT].size, m_opts.height); - } - break; - - case border::ALL: - draw_border(border::TOP); - draw_border(border::BOTTOM); - draw_border(border::LEFT); - draw_border(border::RIGHT); - break; - } -} - -/** - * Draw over- and underline onto the pixmap - */ -void bar::draw_lines(int x, int w) { - if (!m_opts.lineheight) - return; - - if (m_attributes & static_cast(attribute::o)) - draw_util::fill( - m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, m_borders[border::TOP].size, w, m_opts.lineheight); - - if (m_attributes & static_cast(attribute::u)) - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x, - m_opts.height - m_borders[border::BOTTOM].size - m_opts.lineheight, w, m_opts.lineheight); -} - -/** - * Shift the contents of the pixmap horizontally - */ -int bar::draw_shift(int x, int chr_width) { - int delta = chr_width; - - if (m_opts.align == alignment::CENTER) { - int base_x = m_opts.width; - base_x -= m_borders[border::RIGHT].size; - base_x /= 2; - base_x += m_borders[border::LEFT].size; - m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), base_x - x / 2, 0, base_x - (x + chr_width) / 2, - 0, x, m_opts.height); - x = base_x - (x + chr_width) / 2 + x; - delta /= 2; - } else if (m_opts.align == alignment::RIGHT) { - m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), m_opts.width - x, 0, - m_opts.width - x - chr_width, 0, x, m_opts.height); - x = m_opts.width - chr_width - m_borders[border::RIGHT].size; - } - - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), x, 0, m_opts.width - x, m_opts.height); - - // Translate pos of clickable areas - if (m_opts.align != alignment::LEFT) { - for (auto&& action : m_actions) { - if (action.active || action.align != m_opts.align) - continue; - action.start_x -= delta; - action.end_x -= delta; - } - } - - return x; -} - -/** - * Draw text character - */ -void bar::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 chr_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_opts.align == alignment::CENTER && chr_width % 2) - chr_width++; - - auto x = draw_shift(m_xpos, chr_width); - auto y = m_opts.vertical_mid + font->height / 2 - font->descent + font->offset_y; - - // m_log.trace("Draw char(%c, width: %i) at pos(%i,%i)", character, chr_width, x, y); - - if (font->xft != nullptr) { - auto color = m_fontmanager->xftcolor(); - XftDrawString16(m_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); - } - - draw_lines(x, chr_width); - m_xpos += chr_width; -} // }}} - -/** - * Draw text string - */ -void bar::draw_textstring(const char* text, size_t len) { // {{{ - for (size_t n = 0; n < len; n++) { - vector 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 chr_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_opts.align == alignment::CENTER && chr_width % 2) - chr_width++; - - auto x = draw_shift(m_xpos, chr_width); - auto y = m_opts.vertical_mid + font->height / 2 - font->descent + font->offset_y; - - // m_log.trace("Draw char(%c, width: %i) at pos(%i,%i)", character, chr_width, x, y); - - if (font->xft != nullptr) { - auto color = m_fontmanager->xftcolor(); - const FcChar16* drawchars = static_cast(chars.data()); - XftDrawString16(m_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()); - } - - draw_lines(x, chr_width); - m_xpos += chr_width; - } -} // }}} - POLYBAR_NS_END diff --git a/src/components/builder.cpp b/src/components/builder.cpp index 8093c501..e3c3c7a2 100644 --- a/src/components/builder.cpp +++ b/src/components/builder.cpp @@ -248,7 +248,7 @@ void builder::font_close(bool force) { void builder::background(string color) { if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) { color = "#" + color.substr(color.length() - 2); - auto bg = m_bar.background.source(); + auto bg = color_util::hex(m_bar.background); color += bg.substr(bg.length() - (bg.length() < 6 ? 3 : 6)); } else if (color.length() >= 7 && color == "#" + string(color.length() - 1, color[1])) { color = color.substr(0, 4); @@ -279,7 +279,7 @@ void builder::color(string color_) { auto color(color_); if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) { color = "#" + color.substr(color.length() - 2); - auto fg = m_bar.foreground.source(); + auto fg = color_util::hex(m_bar.foreground); color += fg.substr(fg.length() - (fg.length() < 6 ? 3 : 6)); } else if (color.length() >= 7 && color == "#" + string(color.length() - 1, color[1])) { color = color.substr(0, 4); @@ -299,7 +299,7 @@ void builder::color(string color_) { void builder::color_alpha(string alpha_) { auto alpha(alpha_); - string val = m_bar.foreground.source(); + string val = color_util::hex(m_bar.foreground); if (alpha.find("#") == std::string::npos) { alpha = "#" + alpha; } diff --git a/src/components/controller.cpp b/src/components/controller.cpp index 7c3c5189..1401a51b 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -2,11 +2,13 @@ #include #include -#include "x11/color.hpp" #include "components/bar.hpp" - +#include "components/config.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/battery.hpp" #include "modules/bspwm.hpp" @@ -22,12 +24,6 @@ #include "modules/text.hpp" #include "modules/xbacklight.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/string.hpp" @@ -44,7 +40,7 @@ #include "modules/volume.hpp" #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" #endif @@ -499,14 +495,16 @@ void controller::on_unrecognized_action(string input) { * Callback for module content update */ void controller::on_update() { + const bar_settings& bar{m_bar->settings()}; + string contents{""}; - string separator{m_bar->settings().separator}; + string separator{bar.separator}; - string padding_left(m_bar->settings().padding_left, ' '); - string padding_right(m_bar->settings().padding_right, ' '); + string padding_left(bar.padding.left, ' '); + string padding_right(bar.padding.right, ' '); - auto margin_left = m_bar->settings().module_margin_left; - auto margin_right = m_bar->settings().module_margin_right; + auto margin_left = bar.module_margin.left; + auto margin_right = bar.module_margin.right; for (const auto& block : m_eventloop->modules()) { string block_contents; diff --git a/src/components/parser.cpp b/src/components/parser.cpp index 706e05ab..567373c7 100644 --- a/src/components/parser.cpp +++ b/src/components/parser.cpp @@ -1,12 +1,13 @@ -#include "x11/color.hpp" -#include "components/types.hpp" - #include "components/parser.hpp" +#include "components/signals.hpp" +#include "components/types.hpp" #include "utils/math.hpp" #include "utils/string.hpp" POLYBAR_NS +parser::parser(const bar_settings& bar) : m_bar(bar) {} + /** * Parse input data */ @@ -111,11 +112,6 @@ void parser::codeblock(string data) { g_signals::parser::attribute_unset(parse_attr(value[0])); break; - case '!': - if (g_signals::parser::attribute_toggle) - g_signals::parser::attribute_toggle(parse_attr(value[0])); - break; - case 'A': if (isdigit(data[0]) || data[0] == ':') { value = parse_action_cmd(data); @@ -172,8 +168,7 @@ size_t parser::text(string data) { return 2; } else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence if (g_signals::parser::unicode_text_write) - g_signals::parser::unicode_text_write( - (utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f)); + g_signals::parser::unicode_text_write((utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f)); return 3; } else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence if (g_signals::parser::unicode_text_write) @@ -197,20 +192,26 @@ size_t parser::text(string data) { /** * TODO: docstring */ -color parser::parse_color(string s, color fallback) { - if (s.empty() || s == "-") +uint32_t parser::parse_color(string s, uint32_t fallback) { + uint32_t color{0}; + if (s.empty() || s[0] == '-' || (color = color_util::parse(s, fallback)) == fallback) return fallback; - return color::parse(s, fallback); + return color_util::premultiply_alpha(color); } /** * TODO: docstring */ -int parser::parse_fontindex(string s) { - if (s.empty() || s == "-") +int8_t parser::parse_fontindex(string s) { + if (s.empty() || s == "-") { 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 */ string parser::parse_action_cmd(string data) { - auto start = string_util::find_nth(data, 0, ":", 1); - auto end = string_util::find_nth(data, 0, ":", 2); + size_t start, end; + if ((start = data.find(':')) == string::npos) + return ""; + if ((end = data.find(':', start + 1)) == string::npos) + return ""; return string_util::trim(data.substr(start, end), ':'); } diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp new file mode 100644 index 00000000..19728b5f --- /dev/null +++ b/src/components/renderer.cpp @@ -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> configure_renderer(const bar_settings& bar, const vector& 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, + const bar_settings& bar, const vector& fonts) + : m_connection(conn), m_log(logger), m_fontmanager(forward(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, ¶ms, back_pixel, 0); + XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, 0); + XCB_AUX_ADD_PARAM(&mask, ¶ms, backing_store, XCB_BACKING_STORE_WHEN_MAPPED); + XCB_AUX_ADD_PARAM(&mask, ¶ms, colormap, m_colormap); + XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, m_bar.force_docking); + XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS); + // clang-format on + + xutils::pack_values(mask, ¶ms, 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 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, ¶ms, foreground, colors[i - 1]); + XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 0); + + xutils::pack_values(mask, ¶ms, 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 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(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(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(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(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(align)); + m_alignment = align; +} + +void renderer::set_attribute(const attribute attr, bool state) { + m_log.trace_x("renderer: set_attribute(%i, %i)", static_cast(attr), state); + + if (state) { + m_attributes |= static_cast(attr); + } else { + m_attributes ^= static_cast(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& 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(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(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 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(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(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(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 renderer::get_actions() { + return m_actions; +} + +void renderer::debughints() { +#if DEBUG and DRAW_CLICKABLE_AREA_HINTS + map 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 diff --git a/src/components/signals.cpp b/src/components/signals.cpp index 73725100..ecbd6e17 100644 --- a/src/components/signals.cpp +++ b/src/components/signals.cpp @@ -12,18 +12,17 @@ callback g_signals::bar::visibility_change{nullptr}; /** * Signals used to communicate with the input parser */ -callback g_signals::parser::alignment_change{nullptr}; -callback g_signals::parser::attribute_set{nullptr}; -callback g_signals::parser::attribute_unset{nullptr}; -callback g_signals::parser::attribute_toggle{nullptr}; -callback g_signals::parser::action_block_open{nullptr}; -callback g_signals::parser::action_block_close{nullptr}; -callback g_signals::parser::color_change{nullptr}; -callback g_signals::parser::font_change{nullptr}; -callback g_signals::parser::pixel_offset{nullptr}; -callback g_signals::parser::ascii_text_write{nullptr}; -callback g_signals::parser::unicode_text_write{nullptr}; -callback g_signals::parser::string_write{nullptr}; +callback g_signals::parser::alignment_change{nullptr}; +callback g_signals::parser::attribute_set{nullptr}; +callback g_signals::parser::attribute_unset{nullptr}; +callback g_signals::parser::action_block_open{nullptr}; +callback g_signals::parser::action_block_close{nullptr}; +callback g_signals::parser::color_change{nullptr}; +callback g_signals::parser::font_change{nullptr}; +callback g_signals::parser::pixel_offset{nullptr}; +callback g_signals::parser::ascii_text_write{nullptr}; +callback g_signals::parser::unicode_text_write{nullptr}; +callback g_signals::parser::string_write{nullptr}; /** * Signals used to communicate with the tray manager diff --git a/src/drawtypes/progressbar.cpp b/src/drawtypes/progressbar.cpp index 398e7b90..cbd6d6f3 100644 --- a/src/drawtypes/progressbar.cpp +++ b/src/drawtypes/progressbar.cpp @@ -115,9 +115,9 @@ namespace drawtypes { // avoid color bleed if (icon_empty && icon_indicator) { 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(bar.background); 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(bar.foreground); } progressbar->set_empty(move(icon_empty)); diff --git a/src/x11/color.cpp b/src/x11/color.cpp index de818631..d23e4a3b 100644 --- a/src/x11/color.cpp +++ b/src/x11/color.cpp @@ -41,7 +41,7 @@ color::operator XRenderColor() const { } color::operator string() const { - return color_util::hex(m_color); + return color_util::hex(m_color); } color::operator uint32_t() const { diff --git a/src/x11/fonts.cpp b/src/x11/fonts.cpp index 7798c1b6..045375ea 100644 --- a/src/x11/fonts.cpp +++ b/src/x11/fonts.cpp @@ -37,21 +37,7 @@ font_manager::~font_manager() { m_fonts.clear(); } -void font_manager::set_preferred_font(int index) { - 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) { +bool font_manager::load(string name, int8_t fontindex, int8_t offset_y) { if (fontindex != -1 && m_fonts.find(fontindex) != m_fonts.end()) { m_logger.warn("A font with index '%i' has already been loaded, skip...", fontindex); return false; @@ -92,6 +78,20 @@ bool font_manager::load(string name, int fontindex, int offset_y) { 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) { static font_t notfound; if (!m_fonts.empty()) { @@ -108,7 +108,7 @@ font_t& font_manager::match_char(uint16_t chr) { 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) return 0; @@ -142,6 +142,28 @@ XftColor font_manager::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(color); + x.green = color_util::green_channel(color); + x.blue = color_util::blue_channel(color); + x.alpha = color_util::alpha_channel(color); + allocate_color(x, initial_alloc); +} + void font_manager::allocate_color(XRenderColor color, bool initial_alloc) { if (!initial_alloc) 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"); } -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}; m_connection.change_gc(gc, XCB_GC_FONT, values); }