From efba06dc2d6f317039a37b7678f962315c6458ba Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Sun, 30 Oct 2016 02:18:39 +0100 Subject: [PATCH] fix(xft): Allocate xft color by value - Solves the issue with transparency using Xft. - X11 fonts are still affected by the alpha issue. Closes jaagr/lemonbuddy#119 --- include/components/bar.hpp | 16 ++++++------ include/components/builder.hpp | 6 ++--- include/components/x11/color.hpp | 31 ++++++++++++++++++++--- include/components/x11/fontmanager.hpp | 15 ++++++++--- include/components/x11/xlib.hpp | 24 +++++++----------- include/drawtypes/progressbar.hpp | 4 +-- tests/CMakeLists.txt | 3 +++ tests/unit_tests/components/x11/color.cpp | 28 +++++++++++++++----- 8 files changed, 84 insertions(+), 43 deletions(-) diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 2285a1f9..1ce813f1 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -106,9 +106,9 @@ class bar : public xpp::event::sink(bs, "background", m_bar.background.hex())); - m_bar.foreground = color::parse(m_conf.get(bs, "foreground", m_bar.foreground.hex())); - m_bar.linecolor = color::parse(m_conf.get(bs, "linecolor", m_bar.linecolor.hex())); + m_bar.background = color::parse(m_conf.get(bs, "background", m_bar.background.hex_to_rgba())); + m_bar.foreground = color::parse(m_conf.get(bs, "foreground", m_bar.foreground.hex_to_rgba())); + m_bar.linecolor = color::parse(m_conf.get(bs, "linecolor", m_bar.linecolor.hex_to_rgba())); // }}} // Set border values {{{ @@ -779,14 +779,14 @@ class bar : public xpp::event::sink %s)", static_cast(gc_), color_.hex(), color_.rgb()); - - const uint32_t value_list[32]{color_.value()}; - m_connection.change_gc(m_gcontexts.at(gc_), XCB_GC_FOREGROUND, value_list); + "bar: color_change(%i, %s -> %s)", static_cast(gc_), color_.hex_to_rgba(), color_.hex_to_rgb()); if (gc_ == gc::FG) { m_fontmanager->allocate_color(color_); - m_xfont_color = color::parse(color_.rgb()).value(); + m_xfont_color = color_.value(); + } else { + const uint32_t value_list[32]{color_.value()}; + m_connection.change_gc(m_gcontexts.at(gc_), XCB_GC_FOREGROUND, value_list); } } //}}} diff --git a/include/components/builder.hpp b/include/components/builder.hpp index f1cb92b1..8ee2bed3 100644 --- a/include/components/builder.hpp +++ b/include/components/builder.hpp @@ -264,7 +264,7 @@ class builder { void background(string color) { if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) { color = "#" + color.substr(color.length() - 2); - auto bg = m_bar.background.hex(); + auto bg = m_bar.background.hex_to_rgba(); 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); @@ -295,7 +295,7 @@ class builder { auto color(color_); if (color.length() == 2 || (color.find("#") == 0 && color.length() == 3)) { color = "#" + color.substr(color.length() - 2); - auto bg = m_bar.foreground.hex(); + auto bg = m_bar.foreground.hex_to_rgba(); 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); @@ -315,7 +315,7 @@ class builder { void color_alpha(string alpha_) { auto alpha(alpha_); - string val = m_bar.foreground.hex(); + string val = m_bar.foreground.hex_to_rgba(); if (alpha.find("#") == std::string::npos) { alpha = "#" + alpha; } diff --git a/include/components/x11/color.hpp b/include/components/x11/color.hpp index 6ec1f842..2c2de377 100644 --- a/include/components/x11/color.hpp +++ b/include/components/x11/color.hpp @@ -22,7 +22,9 @@ static map g_colorstore; class color { public: explicit color(string hex) : m_hex(string_util::upper(hex)) { - m_rgba.v = static_cast(strtoul(&hex[1], nullptr, 16)); + m_rgba.v = (strtoul(&hex[1], nullptr, 16)); + m_rgb = (m_rgba.v << 8) >> 8; + // premultiply alpha m_rgba._.r = m_rgba._.r * m_rgba._.a / 255; m_rgba._.g = m_rgba._.g * m_rgba._.a / 255; @@ -33,14 +35,34 @@ class color { char buffer[7]; snprintf(buffer, sizeof(buffer), "%06x", v); m_hex = "#" + string{buffer}; - m_rgba.v = v; + m_rgba.v = (strtoul(&m_hex[1], nullptr, 16)); } uint32_t value() const { return m_rgba.v; } - string rgb() const { + uint32_t rgb() const { + return m_rgb; + } + + uint32_t alpha() const { + return 0xFF & (value() >> 24); + } + + uint32_t red() const { + return 0xFF & (rgb() >> 16); + } + + uint32_t green() const { + return 0xFF & (rgb() >> 8); + } + + uint32_t blue() const { + return 0xFF & rgb(); + } + + string hex_to_rgb() const { // clang-format off return string_util::from_stream(stringstream() << "#" @@ -52,7 +74,7 @@ class color { // clang-format on } - string hex() const { + string hex_to_rgba() const { return m_hex; } @@ -82,6 +104,7 @@ class color { protected: rgba m_rgba; + unsigned long m_rgb; string m_hex; }; diff --git a/include/components/x11/fontmanager.hpp b/include/components/x11/fontmanager.hpp index f661a29c..aabeeaed 100644 --- a/include/components/x11/fontmanager.hpp +++ b/include/components/x11/fontmanager.hpp @@ -51,8 +51,8 @@ class fontmanager { explicit fontmanager(connection& conn, const logger& logger) : m_connection(conn), m_logger(logger) { m_display = xlib::get_display(); - m_visual = xlib::get_visual(); - m_colormap = XDefaultColormap(m_display, 0); + m_visual = xlib::get_visual(conn.default_screen()); + m_colormap = xlib::create_colormap(conn.default_screen()); } ~fontmanager() { @@ -170,8 +170,15 @@ class fontmanager { if (!initial_alloc) XftColorFree(m_display, m_visual, m_colormap, &m_xftcolor); - if (!XftColorAllocName(m_display, m_visual, m_colormap, xcolor.rgb().c_str(), &m_xftcolor)) - m_logger.err("Failed to allocate color '%s'", xcolor.hex()); + XRenderColor color; + + color.red = (xcolor.red()) << 8; + color.green = (xcolor.green()) << 8; + color.blue = (xcolor.blue()) << 8; + color.alpha = xcolor.alpha() << 8; + + if (!XftColorAllocValue(m_display, m_visual, m_colormap, &color, &m_xftcolor)) + m_logger.err("Failed to allocate color '%s'", xcolor.hex_to_rgba()); } // }}} void set_gcontext_font(gcontext& gc, xcb_font_t font) { // {{{ diff --git a/include/components/x11/xlib.hpp b/include/components/x11/xlib.hpp index 1b49e075..0f50e019 100644 --- a/include/components/x11/xlib.hpp +++ b/include/components/x11/xlib.hpp @@ -8,7 +8,7 @@ LEMONBUDDY_NS namespace xlib { static Display* g_display = nullptr; - static Visual* g_visual = nullptr; + static XVisualInfo g_visual_info; /** * Get pointer of Xlib Display @@ -22,22 +22,16 @@ namespace xlib { /** * Get pointer of Xlib visual */ - inline Visual* get_visual() { - if (g_visual == nullptr) { - XVisualInfo xv; - xv.depth = 32; - int result = 0; - auto result_ptr = XGetVisualInfo(get_display(), VisualDepthMask, &xv, &result); - - if (result > 0) - g_visual = result_ptr->visual; - else - g_visual = XDefaultVisual(get_display(), 0); - - free(result_ptr); + inline Visual* get_visual(int screen = 0) { + if (g_visual_info.visual == nullptr) { + XMatchVisualInfo(get_display(), screen, 32, TrueColor, &g_visual_info); } - return g_visual; + return g_visual_info.visual; + } + + inline Colormap create_colormap(int screen = 0) { + return XCreateColormap(get_display(), XRootWindow(get_display(), screen), get_visual(), screen); } /** diff --git a/include/drawtypes/progressbar.hpp b/include/drawtypes/progressbar.hpp index 5d3e25ab..0c84a1a3 100644 --- a/include/drawtypes/progressbar.hpp +++ b/include/drawtypes/progressbar.hpp @@ -144,9 +144,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.hex(); + icon_empty->m_background = bar.background.hex_to_rgba(); if (!icon_indicator->m_foreground.empty() && icon_empty->m_foreground.empty()) - icon_empty->m_foreground = bar.foreground.hex(); + icon_empty->m_foreground = bar.foreground.hex_to_rgba(); } progressbar->set_empty(move(icon_empty)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 898311db..e6317366 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,3 +18,6 @@ unit_test("utils/string") unit_test("components/command_line") unit_test("components/di") #unit_test("components/logger") +unit_test("components/x11/color") +#unit_test("components/x11/connection") +#unit_test("components/x11/window") diff --git a/tests/unit_tests/components/x11/color.cpp b/tests/unit_tests/components/x11/color.cpp index f1e9710e..edfb77da 100644 --- a/tests/unit_tests/components/x11/color.cpp +++ b/tests/unit_tests/components/x11/color.cpp @@ -7,8 +7,22 @@ int main() { "color"_test = [] { color test{"#33990022"}; - expect(test.hex() == "#33990022"); - expect(test.rgb() == "#1E0006"); + expect(test.hex_to_rgba() == "#33990022"); + expect(test.hex_to_rgb() == "#1E0006"); + }; + + "channels"_test = [] { + color test{"#eefb9281"}; + expect(test.alpha() == 0xee); + expect(test.red() == 0xfb); + expect(test.green() == 0x92); + expect(test.blue() == 0x81); + }; + + "base"_test = [] { + color test{"#eefb9281"}; + auto hex = test.hex_to_rgb(); + expect(std::strtoul(&hex[0], 0, 16) == 0x000000); }; "cache"_test = [] { @@ -23,13 +37,13 @@ int main() { }; "predefined"_test = [] { - expect(g_colorblack.hex() == "#FF000000"); - expect(g_colorwhite.hex() == "#FFFFFFFF"); + expect(g_colorblack.hex_to_rgba() == "#FF000000"); + expect(g_colorwhite.hex_to_rgba() == "#FFFFFFFF"); }; "parse"_test = [] { - expect(color::parse("#ff9900", g_colorblack).hex() == "#FFFF9900"); - expect(color::parse("invalid", g_colorwhite).hex() == "#FFFFFFFF"); - expect(color::parse("33990022", g_colorwhite).rgb() == "#1E0006"); + expect(color::parse("#ff9900", g_colorblack).hex_to_rgba() == "#FFFF9900"); + expect(color::parse("invalid", g_colorwhite).hex_to_rgba() == "#FFFFFFFF"); + expect(color::parse("33990022", g_colorwhite).hex_to_rgb() == "#1E0006"); }; }