From 25e33b6aabb40acda6956f6dd566f65cd7120713 Mon Sep 17 00:00:00 2001
From: Michael Carlberg <c@rlberg.se>
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<evt::button_press, evt::expose, evt::property_notify> {
  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);
   ~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);
 
  protected:
-  void flush();
   void refresh_window();
-  void load_fonts();
-  void configure_geom();
   void create_monitor();
-  void create_window();
-  void create_pixmap();
-  void create_gcontexts();
+  void configure_geom();
   void restack_window();
   void map_window();
   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::property_notify& evt);
 
-  void on_alignment_change(alignment align);
-  void on_attribute_set(attribute attr);
-  void on_attribute_unset(attribute attr);
-  void on_attribute_toggle(attribute attr);
-  void on_action_block_open(mousebtn btn, string cmd);
-  void on_action_block_close(mousebtn btn);
-  void on_color_change(gc gc_, color color_);
+  void on_alignment_change(const alignment align);
+  void on_attribute_set(const attribute attr);
+  void on_attribute_unset(const attribute attr);
+  void on_attribute_toggle(const attribute attr);
+  void on_action_block_open(const mousebtn btn, string cmd);
+  void on_action_block_close(const mousebtn btn);
+  void on_color_change(const gc gc_, uint32_t color);
   void on_font_change(int index);
   void on_pixel_offset(int px);
   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:
   connection& m_connection;
   const config& m_conf;
   const logger& m_log;
-  unique_ptr<font_manager> m_fontmanager;
   unique_ptr<tray_manager> 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<renderer> m_renderer;
 
   bar_settings m_opts;
-  map<border, border_settings> m_borders;
-  map<gc, gcontext> m_gcontexts;
-  vector<action_block> 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<unique_ptr<bar>> 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> 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
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<string> action_click;
-    extern callback<bool> visibility_change;
+    extern callback<const bool> visibility_change;
   }
 
   namespace parser {
-    extern callback<alignment> alignment_change;
-    extern callback<attribute> attribute_set;
-    extern callback<attribute> attribute_unset;
-    extern callback<attribute> attribute_toggle;
-    extern callback<mousebtn, string> action_block_open;
-    extern callback<mousebtn> action_block_close;
-    extern callback<gc, color> color_change;
-    extern callback<int> font_change;
-    extern callback<int> pixel_offset;
-    extern callback<uint16_t> ascii_text_write;
-    extern callback<uint16_t> unicode_text_write;
-    extern callback<const char*, size_t> string_write;
+    extern callback<const alignment> alignment_change;
+    extern callback<const attribute> attribute_set;
+    extern callback<const attribute> attribute_unset;
+    extern callback<const mousebtn, string> action_block_open;
+    extern callback<const mousebtn> action_block_close;
+    extern callback<const gc, const uint32_t> color_change;
+    extern callback<const int8_t> font_change;
+    extern callback<const int16_t> pixel_offset;
+    extern callback<const uint16_t> ascii_text_write;
+    extern callback<const uint16_t> unicode_text_write;
+    extern callback<const char*, const size_t> string_write;
   }
 
   namespace tray {
-    extern callback<uint16_t> report_slotcount;
-    extern callback<uint32_t> clear_bg;
+    extern callback<const uint16_t> report_slotcount;
+    extern callback<const uint32_t> 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<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 {
-  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 <typename T = uint8_t>
+  template <typename T>
   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<int, font_t> m_fonts;
-  int m_fontindex = -1;
-  XftColor m_xftcolor;
+  map<uint8_t, font_t> m_fonts;
+  int8_t m_fontindex{-1};
+
+  XftColor m_xftcolor{};
+  XftDraw* m_xftdraw{nullptr};
 };
 
 di::injector<unique_ptr<font_manager>> 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<unique_ptr<bar>> configure_bar() {
       configure_connection(),
       configure_config(),
       configure_logger(),
-      configure_font_manager(),
       configure_tray_manager());
   // clang-format on
 }
@@ -39,25 +39,19 @@ di::injector<unique_ptr<bar>> configure_bar() {
 /**
  * Construct bar instance
  */
-bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr<font_manager> font_manager,
-    unique_ptr<tray_manager> tray_manager)
-    : m_connection(conn)
-    , m_conf(config)
-    , m_log(logger)
-    , m_fontmanager(forward<decltype(font_manager)>(font_manager))
-    , m_tray(forward<decltype(tray_manager)>(tray_manager)) {}
+bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr<tray_manager> tray_manager)
+    : m_connection(conn), m_conf(config), m_log(logger), m_tray(forward<decltype(tray_manager)>(tray_manager)) {}
 
 /**
  * Cleanup signal handlers and destroy the bar window
  */
 bar::~bar() {
-  std::lock_guard<concurrency_util::spin_lock> lck(m_lock);
+  std::lock_guard<std::mutex> 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<string>(bs, "separator", ""), '"');
   m_opts.locale = m_conf.get<string>(bs, "locale", "");
+  m_opts.separator = string_util::trim(m_conf.get<string>(bs, "separator", ""), '"');
 
   create_monitor();
 
   // Set bar colors {{{
 
-  m_opts.background = color::parse(m_conf.get<string>(bs, "background", m_opts.background.source()));
-  m_opts.foreground = color::parse(m_conf.get<string>(bs, "foreground", m_opts.foreground.source()));
-  m_opts.linecolor = color::parse(m_conf.get<string>(bs, "linecolor", m_opts.linecolor.source()));
+  m_opts.background = color::parse(m_conf.get<string>(bs, "background", color_util::hex<uint16_t>(m_opts.background)));
+  m_opts.foreground = color::parse(m_conf.get<string>(bs, "foreground", color_util::hex<uint16_t>(m_opts.foreground)));
+  m_opts.linecolor = color::parse(m_conf.get<string>(bs, "linecolor", color_util::hex<uint16_t>(m_opts.linecolor)));
 
   // }}}
   // Set border values {{{
 
   auto bsize = m_conf.get<int>(bs, "border-size", 0);
-  auto bcolor = m_conf.get<string>(bs, "border-color", g_colorempty.source());
+  auto bcolor = m_conf.get<string>(bs, "border-color", "#00000000");
 
-  m_borders.emplace(border::TOP, border_settings{});
-  m_borders[border::TOP].size = m_conf.get<int>(bs, "border-top", bsize);
-  m_borders[border::TOP].color = color::parse(m_conf.get<string>(bs, "border-top-color", bcolor));
+  m_opts.borders.emplace(edge::TOP, border_settings{});
+  m_opts.borders[edge::TOP].size = m_conf.get<int>(bs, "border-top", bsize);
+  m_opts.borders[edge::TOP].color = color::parse(m_conf.get<string>(bs, "border-top-color", bcolor));
 
-  m_borders.emplace(border::BOTTOM, border_settings{});
-  m_borders[border::BOTTOM].size = m_conf.get<int>(bs, "border-bottom", bsize);
-  m_borders[border::BOTTOM].color = color::parse(m_conf.get<string>(bs, "border-bottom-color", bcolor));
+  m_opts.borders.emplace(edge::BOTTOM, border_settings{});
+  m_opts.borders[edge::BOTTOM].size = m_conf.get<int>(bs, "border-bottom", bsize);
+  m_opts.borders[edge::BOTTOM].color = color::parse(m_conf.get<string>(bs, "border-bottom-color", bcolor));
 
-  m_borders.emplace(border::LEFT, border_settings{});
-  m_borders[border::LEFT].size = m_conf.get<int>(bs, "border-left", bsize);
-  m_borders[border::LEFT].color = color::parse(m_conf.get<string>(bs, "border-left-color", bcolor));
+  m_opts.borders.emplace(edge::LEFT, border_settings{});
+  m_opts.borders[edge::LEFT].size = m_conf.get<int>(bs, "border-left", bsize);
+  m_opts.borders[edge::LEFT].color = color::parse(m_conf.get<string>(bs, "border-left-color", bcolor));
 
-  m_borders.emplace(border::RIGHT, border_settings{});
-  m_borders[border::RIGHT].size = m_conf.get<int>(bs, "border-right", bsize);
-  m_borders[border::RIGHT].color = color::parse(m_conf.get<string>(bs, "border-right-color", bcolor));
+  m_opts.borders.emplace(edge::RIGHT, border_settings{});
+  m_opts.borders[edge::RIGHT].size = m_conf.get<int>(bs, "border-right", bsize);
+  m_opts.borders[edge::RIGHT].color = color::parse(m_conf.get<string>(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<bool>(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<int>("global/wm", "margin-top", 0);
-  m_opts.margins.b = m_conf.get<int>("global/wm", "margin-bottom", 0);
+  m_opts.strut.top = m_conf.get<int>("global/wm", "margin-top", 0);
+  m_opts.strut.bottom = m_conf.get<int>("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<string>(bs, "wm-name", m_opts.wmname);
+  m_opts.wmname = m_conf.get<string>(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<string>(bs, "font", {})).create<unique_ptr<renderer>>();
+  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<float>(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<concurrency_util::spin_lock> 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<std::mutex> guard(m_mutex, std::adopt_lock);
 
-#if DEBUG and DRAW_CLICKABLE_AREA_HINTS
-  map<alignment, int> 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<int>(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<string>(m_conf.bar_section(), "font", {});
-
-  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_opts.foreground, true);
-}
-
 /**
  * Configure geometry values
  */
@@ -521,47 +419,47 @@ void bar::configure_geom() {
   auto offsety = m_conf.get<string>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(m_opts.width, 0, m_opts.monitor->w);
-  m_opts.height = math_util::cap<int>(m_opts.height, 0, m_opts.monitor->h);
+  m_opts.size.w = math_util::cap<int>(m_opts.size.w, 0, m_opts.monitor->w);
+  m_opts.size.h = math_util::cap<int>(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, &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_opts.dock);
-  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_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<uint32_t> 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, &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), 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<std::mutex> 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<mousebtn>(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<int>(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<concurrency_util::spin_lock> lck(m_lock);
-  {
-    m_log.trace_x("bar: Received button press event: %i at pos(%i, %i)", static_cast<int>(evt->detail), evt->event_x,
-        evt->event_y);
-
-    mousebtn button = static_cast<mousebtn>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(gc_), color_.source(), static_cast<uint32_t>(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<int>(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<int>(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<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 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<const FcChar16*>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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 <csignal>
 #include <mutex>
 
-#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<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
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<bool> g_signals::bar::visibility_change{nullptr};
 /**
  * Signals used to communicate with the input parser
  */
-callback<alignment> g_signals::parser::alignment_change{nullptr};
-callback<attribute> g_signals::parser::attribute_set{nullptr};
-callback<attribute> g_signals::parser::attribute_unset{nullptr};
-callback<attribute> g_signals::parser::attribute_toggle{nullptr};
-callback<mousebtn, string> g_signals::parser::action_block_open{nullptr};
-callback<mousebtn> g_signals::parser::action_block_close{nullptr};
-callback<gc, color> g_signals::parser::color_change{nullptr};
-callback<int> g_signals::parser::font_change{nullptr};
-callback<int> g_signals::parser::pixel_offset{nullptr};
-callback<uint16_t> g_signals::parser::ascii_text_write{nullptr};
-callback<uint16_t> g_signals::parser::unicode_text_write{nullptr};
-callback<const char*, size_t> g_signals::parser::string_write{nullptr};
+callback<const alignment> g_signals::parser::alignment_change{nullptr};
+callback<const attribute> g_signals::parser::attribute_set{nullptr};
+callback<const attribute> g_signals::parser::attribute_unset{nullptr};
+callback<const mousebtn, string> g_signals::parser::action_block_open{nullptr};
+callback<const mousebtn> g_signals::parser::action_block_close{nullptr};
+callback<const gc, const uint32_t> g_signals::parser::color_change{nullptr};
+callback<const int8_t> g_signals::parser::font_change{nullptr};
+callback<const int16_t> g_signals::parser::pixel_offset{nullptr};
+callback<const uint16_t> g_signals::parser::ascii_text_write{nullptr};
+callback<const uint16_t> g_signals::parser::unicode_text_write{nullptr};
+callback<const char*, const size_t> 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<uint16_t>(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<uint16_t>(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<uint8_t>(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<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) {
   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);
 }