diff --git a/include/cairo/context.hpp b/include/cairo/context.hpp index 82fd4396..d7899100 100644 --- a/include/cairo/context.hpp +++ b/include/cairo/context.hpp @@ -111,6 +111,11 @@ namespace cairo { return *this; } + context& operator<<(const translate& d) { + cairo_translate(m_c, d.x, d.y); + return *this; + } + context& operator<<(const linear_gradient& l) { if (l.steps.size() >= 2) { auto pattern = cairo_pattern_create_linear(l.x1, l.y1, l.x2, l.y2); @@ -274,10 +279,30 @@ namespace cairo { return *this; } + context& mask(cairo_pattern_t* pattern) { + cairo_mask(m_c, pattern); + return *this; + } + + context& pop(cairo_pattern_t** pattern) { + *pattern = cairo_pop_group(m_c); + return *this; + } + + context& push() { + cairo_push_group(m_c); + return *this; + } + + context& destroy(cairo_pattern_t** pattern) { + cairo_pattern_destroy(*pattern); + *pattern = nullptr; + return *this; + } + context& clear() { cairo_save(m_c); - cairo_set_operator(m_c, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(m_c, 0.0, 0.0, 0.0, 0.0); + cairo_set_operator(m_c, CAIRO_OPERATOR_CLEAR); cairo_paint(m_c); cairo_restore(m_c); return *this; diff --git a/include/cairo/types.hpp b/include/cairo/types.hpp index dda2e563..56525e63 100644 --- a/include/cairo/types.hpp +++ b/include/cairo/types.hpp @@ -41,6 +41,10 @@ namespace cairo { double dx; double dy; }; + struct translate { + double x; + double y; + }; struct linear_gradient { double x1; double y1; diff --git a/include/components/renderer.hpp b/include/components/renderer.hpp index 47569c7c..d126fcf1 100644 --- a/include/components/renderer.hpp +++ b/include/components/renderer.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "cairo/fwd.hpp" #include "common.hpp" @@ -21,7 +22,7 @@ class logger; using std::map; struct alignment_block { - xcb_pixmap_t pixmap; + cairo_pattern_t* pattern; double x; double y; }; @@ -58,7 +59,9 @@ class renderer double block_x(alignment a) const; double block_y(alignment a) const; double block_w(alignment a) const; + double block_h(alignment a) const; + void flush(alignment a); void highlight_clickable_areas(); bool on(const signals::ui::request_snapshot& evt); @@ -122,6 +125,8 @@ class renderer bool m_fixedcenter; string m_snapshot_dst; + + cairo_pattern_t* m_cornermask{}; }; POLYBAR_NS_END diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp index 4590e25f..64fb13ff 100644 --- a/src/components/renderer.cpp +++ b/src/components/renderer.cpp @@ -102,15 +102,9 @@ renderer::renderer( m_log.trace("renderer: Allocate alignment blocks"); { - const auto create_block = [&](alignment a) { - auto pid = m_connection.generate_id(); - m_connection.create_pixmap_checked(m_depth, pid, m_pixmap, m_bar.size.w, m_bar.size.h); - m_blocks.emplace(a, alignment_block{pid, 0.0, 0.0}); - }; - - create_block(alignment::LEFT); - create_block(alignment::CENTER); - create_block(alignment::RIGHT); + m_blocks.emplace(alignment::LEFT, alignment_block{nullptr, 0.0, 0.0}); + m_blocks.emplace(alignment::CENTER, alignment_block{nullptr, 0.0, 0.0}); + m_blocks.emplace(alignment::RIGHT, alignment_block{nullptr, 0.0, 0.0}); } m_log.trace("renderer: Allocate cairo components"); @@ -155,7 +149,8 @@ renderer::renderer( auto fonts_loaded = false; for (const auto& f : fonts) { vector fd{string_util::split(f, ';')}; - auto font = cairo::make_font(*m_context, string(fd[0]), fd.size() > 1 ? std::atoi(fd[1].c_str()) : 0, dpi_x, dpi_y); + auto font = + cairo::make_font(*m_context, string(fd[0]), fd.size() > 1 ? std::atoi(fd[1].c_str()) : 0, dpi_x, dpi_y); m_log.info("Loaded font \"%s\" (name=%s, file=%s)", fd[0], font->name(), font->file()); *m_context << move(font); fonts_loaded = true; @@ -214,20 +209,35 @@ void renderer::begin() { m_ul = m_bar.underline.color; m_ol = m_bar.overline.color; + // Clear canvas + m_context->clear(); m_context->save(); - // Clear canvas - m_surface->set_drawable(m_pixmap, m_bar.size.w, m_bar.size.h); - m_context->clear(); + // Create corner mask + if (m_bar.radius != 0.0) { + m_context->save(); + m_context->push(); + // clang-format off + *m_context << cairo::rounded_corners{ + static_cast(m_rect.x), + static_cast(m_rect.y), + static_cast(m_rect.width), + static_cast(m_rect.height), m_bar.radius}; + // clang-format on + *m_context << rgba{1.0, 1.0, 1.0, 1.0}; + m_context->fill(); + m_context->pop(&m_cornermask); + m_context->restore(); + } fill_borders(); // clang-format off m_context->clip(cairo::rect{ - static_cast(m_rect.x), - static_cast(m_rect.y), - static_cast(m_rect.width), - static_cast(m_rect.height)}); + static_cast(m_rect.x), + static_cast(m_rect.y), + static_cast(m_rect.width), + static_cast(m_rect.height)}); // clang-format on fill_background(); @@ -247,8 +257,6 @@ void renderer::end() { rgba bg1{m_bar.background}; bg1.a = 0.0; rgba bg2{m_bar.background}; bg2.a = 1.0; // clang-format on - - m_surface->set_drawable(b.second.pixmap, m_bar.size.w, m_bar.size.h); m_context->save(); *m_context << cairo::linear_gradient{x - 40.0, 0.0, x, 0.0, {bg1, bg2}}; m_context->paint(); @@ -262,39 +270,47 @@ void renderer::end() { a.end_x += block_x(a.align) + m_rect.x; } + flush(m_align); + m_context->restore(); + m_surface->flush(); flush(); } +/** + * Flush contents of given alignment block + */ +void renderer::flush(alignment a) { + if (a != alignment::NONE) { + double x = block_x(a); + double y = block_y(a); + double w = block_w(a); + double h = block_h(a); + + m_surface->flush(); + m_context->pop(&m_blocks[m_align].pattern); + + m_context->save(); + { + *m_context << cairo::rect{m_rect.x + x, m_rect.y + y, w, h}; + *m_context << cairo::translate{x, 0.0}; + *m_context << m_blocks[a].pattern; + m_context->paint(); + m_surface->flush(); + } + m_context->restore(); + m_surface->flush(); + m_context->destroy(&m_blocks[a].pattern); + } +} + /** * Flush pixmap contents onto the target window */ void renderer::flush() { m_log.trace_x("renderer: flush"); - m_surface->set_drawable(m_pixmap, m_bar.size.w, m_bar.size.h); - m_surface->flush(); - - for (auto&& b : m_blocks) { - double x = m_rect.x + block_x(b.first); - double y = m_rect.y + block_y(b.first); - double w = m_rect.x + m_rect.width - x; - double h = m_rect.y + m_rect.height - y; - - if (w > 0.0) { - m_log.trace_x("renderer: copy alignment block (x=%.0f y=%.0f w=%.0f h=%.0f)", x, y, w, h); - m_connection.copy_area(b.second.pixmap, m_pixmap, m_gcontext, m_rect.x, m_rect.y, x, y, w, h); - m_connection.flush(); - } else { - m_log.trace_x("renderer: ignoring empty alignment block"); - continue; - } - } - - m_surface->dirty(); - m_surface->flush(); - highlight_clickable_areas(); #if 0 @@ -363,6 +379,13 @@ double renderer::block_w(alignment a) const { return m_blocks.at(a).x; } +/** + * Get block height for given alignment + */ +double renderer::block_h(alignment) const { + return m_rect.height; +} + /** * Reserve space at given edge */ @@ -405,16 +428,6 @@ void renderer::fill_background() { m_context->save(); *m_context << static_cast(m_comp_bg); - if (m_bar.radius != 0.0) { - // clang-format off - *m_context << cairo::rounded_corners{ - static_cast(m_rect.x), - static_cast(m_rect.y), - static_cast(m_rect.width), - static_cast(m_rect.height), m_bar.radius}; - // clang-format on - } - if (!m_bar.background_steps.empty()) { m_log.trace_x("renderer: gradient background (steps=%lu)", m_bar.background_steps.size()); *m_context << cairo::linear_gradient{0.0, 0.0 + m_rect.y, 0.0, 0.0 + m_rect.height, m_bar.background_steps}; @@ -423,8 +436,8 @@ void renderer::fill_background() { *m_context << m_bar.background; } - if (m_bar.radius != 0.0) { - m_context->fill(); + if (m_cornermask != nullptr) { + m_context->mask(m_cornermask); } else { m_context->paint(); } @@ -621,12 +634,11 @@ bool renderer::on(const signals::parser::change_alignment& evt) { auto align = static_cast(evt.cast()); if (align != m_align) { m_log.trace_x("renderer: change_alignment(%i)", static_cast(align)); + flush(m_align); + m_context->push(); m_align = align; m_blocks[m_align].x = 0.0; m_blocks[m_align].y = 0.0; - m_surface->set_drawable(m_blocks.at(m_align).pixmap, m_bar.size.w, m_bar.size.h); - m_context->clear(); - fill_background(); } return true; }