wip(refactor): Cairo drawing

This commit is contained in:
Michael Carlberg 2017-01-24 06:59:58 +01:00
parent 13633f715d
commit 8b9461e63e
15 changed files with 499 additions and 300 deletions

View File

@ -19,6 +19,9 @@
POLYBAR_NS
namespace cairo {
/**
* @brief Cairo context
*/
class context {
public:
explicit context(const surface& surface, const logger& log) : m_c(cairo_create(surface)), m_log(log) {
@ -99,9 +102,18 @@ namespace cairo {
return *this;
}
context& operator<<(const displace& d) {
cairo_set_operator(m_c, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface(m_c, cairo_get_target(m_c), d.x - d.w, d.y);
cairo_rectangle(m_c, d.x - d.w, d.y, d.x + d.dx, d.y + d.dy);
cairo_fill(m_c);
cairo_surface_flush(cairo_get_target(m_c));
return *this;
}
context& operator<<(const linear_gradient& l) {
if (l.steps.size() >= 2) {
auto pattern = cairo_pattern_create_linear(l.x0, l.y0, l.x1, l.y1);
auto pattern = cairo_pattern_create_linear(l.x1, l.y1, l.x2, l.y2);
*this << pattern;
auto stops = l.steps.size();
auto step = 1.0 / (stops - 1);
@ -149,7 +161,7 @@ namespace cairo {
});
string utf8 = string(t.contents);
unicode_charlist chars;
utils::unicode_charlist chars;
utils::utf8_to_ucs4((const unsigned char*)utf8.c_str(), chars);
while (!chars.empty()) {
@ -167,55 +179,32 @@ namespace cairo {
end++;
}
// encapsulate the vert. centering
save();
// Use the font
f->use();
// Get subset extents
cairo_text_extents_t extents;
f->textwidth(subset, &extents);
save(true);
{
*this << abspos{x, y};
f->use();
cairo_text_extents_t extents;
f->textwidth(utf8, &extents);
double dx{0.0}, dy{0.0};
if (t.align == alignment::CENTER) {
dx -= extents.x_advance / 2.0;
save();
{
cairo_rectangle(m_c, x, y, extents.x_advance / 2.0, extents.height);
auto pattern = cairo_pattern_create_rgba(1.0, 0.0, 0.0, 0.0);
cairo_set_source(m_c, pattern);
cairo_translate(m_c, dx, 0.0);
cairo_set_operator(m_c, CAIRO_OPERATOR_DEST);
cairo_fill(m_c);
cairo_pattern_destroy(pattern);
}
restore();
} else if (t.align == alignment::RIGHT) {
dx -= extents.x_advance;
save();
{
cairo_rectangle(m_c, x, y, extents.x_advance, extents.height);
auto pattern = cairo_pattern_create_rgba(0.0, 0.0, 0.0, 0.0);
cairo_set_source(m_c, pattern);
cairo_set_operator(m_c, CAIRO_OPERATOR_DEST);
cairo_translate(m_c, dx, 0.0);
cairo_fill(m_c);
cairo_pattern_destroy(pattern);
}
restore();
}
auto fontextents = f->extents();
f->render(subset, x + dx,
y + dy - (extents.height / 2.0 + extents.y_bearing + fontextents.descent) + f->offset());
position(&x, nullptr);
x += dx;
y += dy;
*this << t.bg;
cairo_set_operator(m_c, static_cast<cairo_operator_t>(t.bg_operator));
cairo_rectangle(m_c, t.bg_rect.x, t.bg_rect.y, t.bg_rect.w + extents.x_advance, t.bg_rect.h);
cairo_fill(m_c);
}
restore();
restore(true);
// Render subset
auto fontextents = f->extents();
f->render(subset, x, y - (extents.height / 2.0 + extents.y_bearing + fontextents.descent) + f->offset());
// Get updated position
position(&x, nullptr);
// Increase position
*t.x_advance += extents.x_advance;
*t.y_advance += extents.y_advance;
chars.erase(chars.begin(), end);
break;
@ -227,7 +216,7 @@ namespace cairo {
continue;
}
char unicode[5]{'\0'};
char unicode[6]{'\0'};
utils::ucs4_to_utf8(unicode, chars.begin()->codepoint);
m_log.warn("Dropping unmatched character %s (U+%04x)", unicode, chars.begin()->codepoint);
utf8.erase(chars.begin()->offset, chars.begin()->length);
@ -237,7 +226,7 @@ namespace cairo {
chars.erase(chars.begin(), ++chars.begin());
}
*this << abspos{x, y};
// *this << abspos{x, y};
return *this;
}
@ -247,7 +236,7 @@ namespace cairo {
return *this;
}
context& save(bool save_point = true) {
context& save(bool save_point = false) {
if (save_point) {
m_points.emplace_front(make_pair<double, double>(0.0, 0.0));
position(&m_points.front().first, &m_points.front().second);
@ -256,9 +245,9 @@ namespace cairo {
return *this;
}
context& restore(bool restore_point = true) {
context& restore(bool restore_point = false) {
cairo_restore(m_c);
if (restore_point) {
if (restore_point && !m_points.empty()) {
*this << abspos{m_points.front().first, m_points.front().first};
m_points.pop_front();
}
@ -284,9 +273,19 @@ namespace cairo {
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_paint(m_c);
cairo_restore(m_c);
return *this;
}
context& clip(const rect& r) {
*this << r;
cairo_clip(m_c);
cairo_new_path(m_c);
return *this;
}

View File

@ -1,10 +1,9 @@
#pragma once
#include <cairo/cairo-ft.h>
#include <list>
#include <set>
#include <string>
#include "cairo/types.hpp"
#include "cairo/utils.hpp"
#include "common.hpp"
#include "errors.hpp"
#include "settings.hpp"
@ -15,47 +14,10 @@
POLYBAR_NS
namespace cairo {
namespace details {
/**
* @brief Global pointer to the Freetype library handler
*/
static FT_Library g_ftlib;
/**
* @brief RAII wrapper used to access the underlying
* FT_Face of a scaled font face
*/
class ft_face_lock {
public:
explicit ft_face_lock(cairo_scaled_font_t* font) : m_font(font) {
m_face = cairo_ft_scaled_font_lock_face(m_font);
}
~ft_face_lock() {
cairo_ft_scaled_font_unlock_face(m_font);
}
operator FT_Face() const {
return m_face;
}
private:
cairo_scaled_font_t* m_font;
FT_Face m_face;
};
}
/**
* @brief Unicode character containing converted codepoint
* and details on where its position in the source string
* @brief Global pointer to the Freetype library handler
*/
struct unicode_character {
explicit unicode_character() : codepoint(0), offset(0), length(0) {}
unsigned long codepoint;
int offset;
int length;
};
using unicode_charlist = std::list<unicode_character>;
static FT_Library g_ftlib;
/**
* @brief Abstract font face
@ -76,8 +38,8 @@ namespace cairo {
cairo_set_font_face(m_cairo, cairo_font_face_reference(m_font_face));
}
virtual size_t match(unicode_charlist& charlist) = 0;
virtual size_t render(const string& text, double x = 0.0, double y = 0.0, bool reverse = false) = 0;
virtual size_t match(utils::unicode_charlist& charlist) = 0;
virtual size_t render(const string& text, double x = 0.0, double y = 0.0) = 0;
virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0;
protected:
@ -109,7 +71,7 @@ namespace cairo {
throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status));
}
auto lock = make_unique<details::ft_face_lock>(m_scaled);
auto lock = make_unique<utils::ft_face_lock>(m_scaled);
auto face = static_cast<FT_Face>(*lock);
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) {
@ -166,8 +128,8 @@ namespace cairo {
cairo_set_scaled_font(m_cairo, m_scaled);
}
size_t match(unicode_charlist& charlist) override {
auto lock = make_unique<details::ft_face_lock>(m_scaled);
size_t match(utils::unicode_charlist& charlist) override {
auto lock = make_unique<utils::ft_face_lock>(m_scaled);
auto face = static_cast<FT_Face>(*lock);
size_t available_chars = 0;
for (auto&& c : charlist) {
@ -181,16 +143,12 @@ namespace cairo {
return available_chars;
}
size_t render(const string& text, double x = 0.0, double y = 0.0, bool reverse = false) override {
size_t render(const string& text, double x = 0.0, double y = 0.0) override {
cairo_glyph_t* glyphs{nullptr};
cairo_text_cluster_t* clusters{nullptr};
cairo_text_cluster_flags_t cf{};
int nglyphs = 0, nclusters = 0;
if (reverse) {
cf = CAIRO_TEXT_CLUSTER_FLAG_BACKWARD;
}
string utf8 = string(text);
auto status = cairo_scaled_font_text_to_glyphs(
m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf);
@ -222,12 +180,16 @@ namespace cairo {
}
if (bytes) {
// auto lock = make_unique<utils::device_lock>(cairo_surface_get_device(cairo_get_target(m_cairo)));
// if (lock.get()) {
// cairo_glyph_path(m_cairo, glyphs, nglyphs);
// }
cairo_text_extents_t extents{};
cairo_scaled_font_glyph_extents(m_scaled, glyphs, nglyphs, &extents);
cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf);
cairo_rel_move_to(m_cairo, extents.x_advance, 0.0);
// cairo_glyph_path(m_cairo, glyphs, nglyphs);
cairo_fill_preserve(m_cairo);
cairo_fill(m_cairo);
cairo_move_to(m_cairo, x + extents.x_advance, 0.0);
}
cairo_glyph_free(glyphs);
@ -276,12 +238,12 @@ namespace cairo {
static bool fc_init{false};
if (!fc_init && !(fc_init = FcInit())) {
throw application_error("Could not load fontconfig");
} else if (FT_Init_FreeType(&details::g_ftlib) != FT_Err_Ok) {
} else if (FT_Init_FreeType(&g_ftlib) != FT_Err_Ok) {
throw application_error("Could not load FreeType");
}
static auto fc_cleanup = scope_util::make_exit_handler([] {
FT_Done_FreeType(details::g_ftlib);
FT_Done_FreeType(g_ftlib);
FcFini();
});

View File

@ -4,11 +4,16 @@
#include "cairo/types.hpp"
#include "common.hpp"
#include "errors.hpp"
#include "utils/color.hpp"
#include "utils/string.hpp"
POLYBAR_NS
namespace cairo {
/**
* @brief Base surface
*/
class surface {
public:
explicit surface(cairo_surface_t* s) : m_s(s) {}
@ -20,30 +25,51 @@ namespace cairo {
return m_s;
}
surface& flush() {
void flush() {
cairo_surface_flush(m_s);
return *this;
}
surface& dirty() {
void show(bool clear = true) {
if (clear) {
cairo_surface_show_page(m_s);
} else {
cairo_surface_copy_page(m_s);
}
}
void dirty() {
cairo_surface_mark_dirty(m_s);
return *this;
}
surface& dirty(const rect& r) {
void dirty(const rect& r) {
cairo_surface_mark_dirty_rectangle(m_s, r.x, r.y, r.w, r.h);
return *this;
}
void write_png(const string& dst) {
auto status = cairo_surface_write_to_png(m_s, dst.c_str());
if (status != CAIRO_STATUS_SUCCESS) {
throw application_error(sstream() << "cairo_surface_write_to_png(): " << cairo_status_to_string(status));
}
}
protected:
cairo_surface_t* m_s;
};
/**
* @brief Surface for xcb
*/
class xcb_surface : public surface {
public:
explicit xcb_surface(xcb_connection_t* c, xcb_pixmap_t p, xcb_visualtype_t* v, int w, int h)
: surface(cairo_xcb_surface_create(c, p, v, w, h)) {}
~xcb_surface() override {}
void set_drawable(xcb_drawable_t d, int w, int h) {
cairo_surface_flush(m_s);
cairo_xcb_surface_set_drawable(m_s, d, w, h);
}
};
}

View File

@ -7,6 +7,10 @@ POLYBAR_NS
enum class alignment;
namespace cairo {
struct point {
double x;
double y;
};
struct abspos {
double x;
double y;
@ -16,14 +20,12 @@ namespace cairo {
double x;
double y;
};
struct rect {
double x;
double y;
double w;
double h;
};
struct line {
double x1;
double y1;
@ -31,15 +33,21 @@ namespace cairo {
double y2;
double w;
};
struct displace {
double x;
double y;
double w;
double h;
double dx;
double dy;
};
struct linear_gradient {
double x0;
double y0;
double x1;
double y1;
double x2;
double y2;
vector<unsigned int> steps;
};
struct rounded_corners {
double x;
double y;
@ -47,11 +55,15 @@ namespace cairo {
double h;
double radius;
};
struct textblock {
alignment align;
string contents;
int fontindex;
unsigned int bg;
int bg_operator;
rect bg_rect;
double *x_advance;
double *y_advance;
};
}

View File

@ -1,9 +1,9 @@
#pragma once
#include <cairo/cairo.h>
#include <map>
#include <list>
#include <set>
#include "cairo/font.hpp"
#include "common.hpp"
#include "utils/string.hpp"
@ -11,6 +11,66 @@ POLYBAR_NS
namespace cairo {
namespace utils {
/**
* @brief RAII wrapper used acquire cairo_device_t
*/
class device_lock {
public:
explicit device_lock(cairo_device_t* device) {
auto status = cairo_device_acquire(device);
if (status == CAIRO_STATUS_SUCCESS) {
m_device = device;
}
}
~device_lock() {
cairo_device_release(m_device);
}
operator bool() const {
return m_device != nullptr;
}
operator cairo_device_t*() const {
return m_device;
}
private:
cairo_device_t* m_device{nullptr};
};
/**
* @brief RAII wrapper used to access the underlying
* FT_Face of a scaled font face
*/
class ft_face_lock {
public:
explicit ft_face_lock(cairo_scaled_font_t* font) : m_font(font) {
m_face = cairo_ft_scaled_font_lock_face(m_font);
}
~ft_face_lock() {
cairo_ft_scaled_font_unlock_face(m_font);
}
operator FT_Face() const {
return m_face;
}
private:
cairo_scaled_font_t* m_font;
FT_Face m_face;
};
/**
* @brief Unicode character containing converted codepoint
* and details on where its position in the source string
*/
struct unicode_character {
explicit unicode_character() : codepoint(0), offset(0), length(0) {}
unsigned long codepoint;
int offset;
int length;
};
using unicode_charlist = std::list<unicode_character>;
/**
* @see <cairo/cairo.h>
*/

View File

@ -45,7 +45,7 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
unique_ptr<inotify_watch>&&);
~controller();
bool run(bool writeback = false);
bool run(bool writeback, string snapshot_dst);
bool enqueue(event&& evt);
bool enqueue(string&& input_data);
@ -84,6 +84,11 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
*/
std::atomic<bool> m_process_events{false};
/**
* @brief Destination path of generated snapshot
*/
string m_snapshot_dst;
/**
* @brief Controls weather the output gets printed to stdout
*/
@ -133,6 +138,11 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
* @brief Thread for the eventqueue loop
*/
std::thread m_event_thread;
/**
* @brief Misc threads
*/
vector<std::thread> m_threads;
};
POLYBAR_NS_END

View File

@ -21,8 +21,14 @@ class logger;
using std::map;
struct alignment_block {
xcb_pixmap_t pixmap;
double x;
double y;
};
class renderer
: public signal_receiver<SIGN_PRIORITY_RENDERER, signals::parser::change_background,
: public signal_receiver<SIGN_PRIORITY_RENDERER, signals::ui::request_snapshot, signals::parser::change_background,
signals::parser::change_foreground, signals::parser::change_underline, signals::parser::change_overline,
signals::parser::change_font, signals::parser::change_alignment, signals::parser::offset_pixel,
signals::parser::attribute_set, signals::parser::attribute_unset, signals::parser::attribute_toggle,
@ -50,9 +56,13 @@ class renderer
void draw_text(const string& contents);
protected:
void adjust_clickable_areas(double width);
double block_x(alignment a) const;
double block_y(alignment a) const;
double block_w(alignment a) const;
void highlight_clickable_areas();
bool on(const signals::ui::request_snapshot& evt);
bool on(const signals::parser::change_background& evt);
bool on(const signals::parser::change_foreground& evt);
bool on(const signals::parser::change_underline& evt);
@ -80,9 +90,6 @@ class renderer
const logger& m_log;
const bar_settings& m_bar;
xcb_rectangle_t m_rect{0, 0, 0U, 0U};
reserve_area m_cleararea{};
int m_depth{32};
xcb_window_t m_window;
xcb_colormap_t m_colormap;
@ -90,31 +97,32 @@ class renderer
xcb_gcontext_t m_gcontext;
xcb_pixmap_t m_pixmap;
vector<action_block> m_actions;
xcb_rectangle_t m_rect{0, 0, 0U, 0U};
reserve_area m_cleararea{};
// bool m_autosize{false};
unique_ptr<cairo::context> m_context;
unique_ptr<cairo::surface> m_surface;
unique_ptr<cairo::xcb_surface> m_surface;
map<alignment, alignment_block> m_blocks;
cairo_operator_t m_compositing_background;
cairo_operator_t m_compositing_foreground;
cairo_operator_t m_compositing_overline;
cairo_operator_t m_compositing_underline;
cairo_operator_t m_compositing_borders;
cairo_operator_t m_comp_bg;
cairo_operator_t m_comp_fg;
cairo_operator_t m_comp_ol;
cairo_operator_t m_comp_ul;
cairo_operator_t m_comp_border;
alignment m_alignment{alignment::NONE};
std::bitset<2> m_attributes;
alignment m_align;
std::bitset<3> m_attr;
int m_font;
unsigned int m_bg;
unsigned int m_fg;
unsigned int m_ol;
unsigned int m_ul;
vector<action_block> m_actions;
int m_fontindex;
unsigned int m_color_background;
unsigned int m_color_foreground;
unsigned int m_color_overline;
unsigned int m_color_underline;
double m_x{0.0};
double m_y{0.0};
bool m_fixedcenter;
string m_snapshot_dst;
};
POLYBAR_NS_END

View File

@ -39,17 +39,7 @@ enum class syntaxtag {
u, // underline color
};
enum class mousebtn {
NONE = 0,
LEFT,
MIDDLE,
RIGHT,
SCROLL_UP,
SCROLL_DOWN,
DOUBLE_LEFT,
DOUBLE_MIDDLE,
DOUBLE_RIGHT
};
enum class mousebtn { NONE = 0, LEFT, MIDDLE, RIGHT, SCROLL_UP, SCROLL_DOWN, DOUBLE_LEFT, DOUBLE_MIDDLE, DOUBLE_RIGHT };
enum class strut {
LEFT = 0,

View File

@ -111,6 +111,9 @@ namespace signals {
struct unshade_window : public detail::base_signal<unshade_window> {
using base_type::base_type;
};
struct request_snapshot : public detail::value_signal<request_snapshot, string> {
using base_type::base_type;
};
}
namespace ui_tray {

View File

@ -35,6 +35,7 @@ namespace signals {
struct dim_window;
struct shade_window;
struct unshade_window;
struct request_snapshot;
}
namespace ui_tray {
struct mapped_clients;

View File

@ -208,7 +208,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.foreground = parse_or_throw("foreground", color_util::hex<uint16_t>(m_opts.foreground));
// Load over-/underline
auto line_color = m_conf.get(bs, "line-color", "#f00"s);
auto line_color = m_conf.get(bs, "line-color", "#ffff0000"s);
auto line_size = m_conf.get(bs, "line-size", 0);
m_opts.overline.size = m_conf.get(bs, "overline-size", line_size);
@ -217,7 +217,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.underline.color = parse_or_throw("underline-color", line_color);
// Load border settings
auto border_color = m_conf.get(bs, "border-color", ""s);
auto border_color = m_conf.get(bs, "border-color", "#00000000"s);
auto border_size = m_conf.get(bs, "border-size", 0);
m_opts.borders.emplace(edge::TOP, border_settings{});
@ -494,11 +494,13 @@ void bar::handle(const evt::destroy_notify& evt) {
* _NET_WM_WINDOW_OPACITY atom value
*/
void bar::handle(const evt::enter_notify&) {
#if 0
#ifdef DEBUG_SHADED
if (m_opts.origin == edge::TOP) {
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::unshade_window{}); });
return;
}
#endif
#endif
if (m_opts.dimmed) {
@ -518,11 +520,13 @@ void bar::handle(const evt::enter_notify&) {
* _NET_WM_WINDOW_OPACITY atom value
*/
void bar::handle(const evt::leave_notify&) {
#if 0
#ifdef DEBUG_SHADED
if (m_opts.origin == edge::TOP) {
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::shade_window{}); });
return;
}
#endif
#endif
if (!m_opts.dimmed) {

View File

@ -47,10 +47,10 @@ string builder::flush() {
if (m_tags[syntaxtag::u]) {
underline_color_close();
}
if ((m_attributes >> static_cast<int>(attribute::UNDERLINE)) & 1U) {
if ((m_attributes >> static_cast<int>(attribute::UNDERLINE)) & 1) {
underline_close();
}
if ((m_attributes >> static_cast<int>(attribute::OVERLINE)) & 1U) {
if ((m_attributes >> static_cast<int>(attribute::OVERLINE)) & 1) {
overline_close();
}

View File

@ -23,6 +23,8 @@
#include "x11/tray_manager.hpp"
#include "x11/types.hpp"
#include <signal.h>
POLYBAR_NS
array<int, 2> g_eventpipe{{-1, -1}};
@ -37,6 +39,10 @@ void interrupt_handler(int signum) {
}
}
void handler(int signum) {
printf("atestin %i\n", signum);
}
/**
* Build controller instance
*/
@ -141,15 +147,25 @@ controller::~controller() {
m_log.info("Deconstruction of %s took %lu ms.", module_name, cleanup_ms);
}
}
m_log.trace("controller: Joining threads");
for (auto&& t : m_threads) {
if (t.joinable()) {
t.join();
}
}
}
/**
* Run the main loop
*/
bool controller::run(bool writeback) {
bool controller::run(bool writeback, string snapshot_dst) {
m_log.info("Starting application");
assert(!m_connection.connection_has_error());
m_writeback = writeback;
m_snapshot_dst = move(snapshot_dst);
m_sig.attach(this);
size_t started_modules{0};
@ -434,6 +450,7 @@ bool controller::process_update(bool force) {
bool is_left = false;
bool is_center = false;
bool is_right = false;
bool is_first = true;
if (block.first == alignment::LEFT) {
is_left = true;
@ -448,7 +465,14 @@ bool controller::process_update(bool force) {
continue;
}
string module_contents{module->contents()};
string module_contents;
try {
module_contents = module->contents();
} catch (const exception& err) {
m_log.err("Failed to get contents for \"%s\" (err: %s)", module->name(), err.what());
}
if (module_contents.empty()) {
continue;
}
@ -461,12 +485,14 @@ bool controller::process_update(bool force) {
block_contents += separator;
}
if (!block_contents.empty() && !margin_left.empty() && !(is_left && module == block.second.front())) {
if (!block_contents.empty() && !margin_left.empty() && !(is_left && is_first)) {
block_contents += margin_left;
}
block_contents.reserve(module_contents.size());
block_contents += module_contents;
is_first = false;
}
if (block_contents.empty()) {
@ -558,6 +584,15 @@ bool controller::on(const signals::eventqueue::check_state&) {
bool controller::on(const signals::ui::ready&) {
m_process_events = true;
enqueue(make_update_evt(true));
if (!m_snapshot_dst.empty()) {
m_threads.emplace_back(thread([&]{
this_thread::sleep_for(3s);
m_sig.emit(signals::ui::request_snapshot{move(m_snapshot_dst)});
enqueue(make_update_evt(true));
}));
}
// let the event bubble
return false;
}

View File

@ -92,13 +92,13 @@ renderer::renderer(
// clang-format on
}
m_log.trace("renderer: Allocate window pixmap");
m_log.trace("renderer: Allocate window pixmaps");
{
m_pixmap = m_connection.generate_id();
m_connection.create_pixmap(m_depth, m_pixmap, m_window, m_bar.size.w, m_bar.size.h);
}
m_log.trace("renderer: Allocate graphic context");
m_log.trace("renderer: Allocate graphic contexts");
{
unsigned int mask{0};
unsigned int value_list[32]{0};
@ -110,10 +110,23 @@ renderer::renderer(
m_connection.create_gc(m_gcontext, m_pixmap, mask, value_list);
}
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_log.trace("renderer: Allocate cairo components");
{
m_surface = make_unique<cairo::xcb_surface>(m_connection, m_pixmap, m_visual, m_bar.size.w, m_bar.size.h);
m_context = make_unique<cairo::context>(*m_surface.get(), m_log);
m_context = make_unique<cairo::context>(*m_surface, m_log);
}
m_log.trace("renderer: Load fonts");
@ -138,16 +151,13 @@ renderer::renderer(
}
}
m_compositing_background =
cairo::utils::str2operator(m_conf.get("settings", "compositing-background", ""s), CAIRO_OPERATOR_SOURCE);
m_compositing_foreground =
cairo::utils::str2operator(m_conf.get("settings", "compositing-foreground", ""s), CAIRO_OPERATOR_OVER);
m_compositing_overline =
cairo::utils::str2operator(m_conf.get("settings", "compositing-overline", ""s), CAIRO_OPERATOR_OVER);
m_compositing_underline =
cairo::utils::str2operator(m_conf.get("settings", "compositing-underline", ""s), CAIRO_OPERATOR_OVER);
m_compositing_borders =
cairo::utils::str2operator(m_conf.get("settings", "compositing-border", ""s), CAIRO_OPERATOR_SOURCE);
m_comp_bg = cairo::utils::str2operator(m_conf.get("settings", "compositing-background", ""s), CAIRO_OPERATOR_SOURCE);
m_comp_fg = cairo::utils::str2operator(m_conf.get("settings", "compositing-foreground", ""s), CAIRO_OPERATOR_OVER);
m_comp_ol = cairo::utils::str2operator(m_conf.get("settings", "compositing-overline", ""s), CAIRO_OPERATOR_OVER);
m_comp_ul = cairo::utils::str2operator(m_conf.get("settings", "compositing-underline", ""s), CAIRO_OPERATOR_OVER);
m_comp_border = cairo::utils::str2operator(m_conf.get("settings", "compositing-border", ""s), CAIRO_OPERATOR_SOURCE);
m_fixedcenter = m_conf.get(m_conf.section(), "fixed-center", true);
}
/**
@ -179,27 +189,22 @@ void renderer::begin() {
// Reset state
m_actions.clear();
m_attributes.reset();
m_alignment = alignment::NONE;
m_attr.reset();
m_align = alignment::NONE;
m_rect = m_bar.inner_area();
m_x = 0.0;
// Reset colors
m_color_background = 0;
m_color_foreground = m_bar.foreground;
m_color_underline = m_bar.underline.color;
m_color_overline = m_bar.overline.color;
m_bg = 0;
m_fg = m_bar.foreground;
m_ul = m_bar.underline.color;
m_ol = m_bar.overline.color;
m_context->save();
// Clear canvas
m_context->save();
*m_context << CAIRO_OPERATOR_SOURCE;
*m_context << rgba{0.0, 0.0, 0.0, 0.0};
m_context->paint();
m_context->restore();
m_surface->set_drawable(m_pixmap, m_bar.size.w, m_bar.size.h);
m_context->clear();
m_context->save();
fill_background();
fill_borders();
// clang-format off
@ -209,6 +214,8 @@ void renderer::begin() {
static_cast<double>(m_rect.width),
static_cast<double>(m_rect.height)});
// clang-format on
fill_background();
}
/**
@ -217,10 +224,30 @@ void renderer::begin() {
void renderer::end() {
m_log.trace_x("renderer: end");
highlight_clickable_areas();
for (auto&& b : m_blocks) {
if (block_x(b.first) + block_w(b.first) > (m_rect.width)) {
double x = m_rect.x + block_x(b.first) + block_w(b.first);
if ((x -= x - m_rect.x - m_rect.width + block_x(b.first)) > 0.0) {
// clang-format off
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();
m_context->restore();
}
}
}
for (auto&& a : m_actions) {
a.start_x += block_x(a.align) + m_rect.x;
a.end_x += block_x(a.align) + m_rect.x;
}
m_context->restore();
m_surface->flush();
flush();
}
@ -231,6 +258,31 @@ void renderer::end() {
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
#ifdef DEBUG_SHADED
if (m_bar.shaded && m_bar.origin == edge::TOP) {
m_log.trace_x(
@ -246,11 +298,54 @@ void renderer::flush() {
m_connection.flush();
return;
}
#endif
#endif
m_log.trace_x("renderer: copy pixmap (geom=%dx%d+%d+%d)", m_rect.width, m_rect.height, m_rect.x, m_rect.y);
m_connection.copy_area(m_pixmap, m_window, m_gcontext, 0, 0, 0, 0, m_bar.size.w, m_bar.size.h);
m_connection.flush();
if (!m_snapshot_dst.empty()) {
try {
m_surface->write_png(m_snapshot_dst);
m_log.info("Successfully wrote %s", m_snapshot_dst);
} catch (const exception& err) {
m_log.err("Failed to write snapshot (err: %s)", err.what());
}
m_snapshot_dst.clear();
}
}
/**
* Get x position of block for given alignment
*/
double renderer::block_x(alignment a) const {
switch (a) {
case alignment::CENTER:
if (!m_fixedcenter || m_rect.width / 2.0 + block_w(a) / 2.0 > m_rect.width - block_w(alignment::RIGHT)) {
return std::max((m_rect.width - block_w(alignment::RIGHT) + block_w(alignment::LEFT)) / 2.0 - block_w(a) / 2.0,
block_w(alignment::LEFT));
} else {
return std::max(m_rect.width / 2.0 - block_w(a) / 2.0, block_w(alignment::LEFT));
}
case alignment::RIGHT:
return std::max(m_rect.width - block_w(a), block_x(alignment::CENTER) + block_w(alignment::CENTER));
default:
return 0.0;
}
}
/**
* Get y position of block for given alignment
*/
double renderer::block_y(alignment) const {
return 0.0;
}
/**
* Get block width for given alignment
*/
double renderer::block_w(alignment a) const {
return m_blocks.at(a).x;
}
/**
@ -293,7 +388,7 @@ void renderer::reserve_space(edge side, unsigned int w) {
*/
void renderer::fill_background() {
m_context->save();
*m_context << m_compositing_background;
*m_context << m_comp_bg;
if (m_bar.radius != 0.0) {
// clang-format off
@ -326,11 +421,11 @@ void renderer::fill_background() {
* Fill overline color
*/
void renderer::fill_overline(double x, double w) {
if (m_bar.overline.size && m_attributes.test(static_cast<int>(attribute::OVERLINE))) {
if (m_bar.overline.size && m_attr.test(static_cast<int>(attribute::OVERLINE))) {
m_log.trace_x("renderer: overline(x=%f, w=%f)", x, w);
m_context->save();
*m_context << m_compositing_overline;
*m_context << m_color_overline;
*m_context << m_comp_ol;
*m_context << m_ol;
*m_context << cairo::rect{x, static_cast<double>(m_rect.y), w, static_cast<double>(m_bar.overline.size)};
m_context->fill();
m_context->restore();
@ -341,11 +436,11 @@ void renderer::fill_overline(double x, double w) {
* Fill underline color
*/
void renderer::fill_underline(double x, double w) {
if (m_bar.underline.size && m_attributes.test(static_cast<int>(attribute::UNDERLINE))) {
if (m_bar.underline.size && m_attr.test(static_cast<int>(attribute::UNDERLINE))) {
m_log.trace_x("renderer: underline(x=%f, w=%f)", x, w);
m_context->save();
*m_context << m_compositing_underline;
*m_context << m_color_underline;
*m_context << m_comp_ul;
*m_context << m_ul;
*m_context << cairo::rect{x, static_cast<double>(m_rect.y + m_rect.height - m_bar.underline.size), w,
static_cast<double>(m_bar.underline.size)};
m_context->fill();
@ -358,7 +453,7 @@ void renderer::fill_underline(double x, double w) {
*/
void renderer::fill_borders() {
m_context->save();
*m_context << m_compositing_borders;
*m_context << m_comp_border;
cairo::rect top{0.0, 0.0, 0.0, 0.0};
top.x += m_bar.borders.at(edge::LEFT).size;
@ -397,54 +492,44 @@ void renderer::fill_borders() {
void renderer::draw_text(const string& contents) {
m_log.trace_x("renderer: text(%s)", contents.c_str());
cairo::abspos origin{static_cast<double>(m_rect.x), static_cast<double>(m_rect.y)};
origin.y += m_rect.height / 2.0;
cairo::abspos origin{};
origin.x = m_rect.x + m_blocks[m_align].x;
origin.y = m_rect.y + m_rect.height / 2.0;
if (m_alignment == alignment::CENTER) {
origin.x += m_rect.width / 2.0;
// adjust_clickable_areas(extents.width / 2.0);
} else if (m_alignment == alignment::RIGHT) {
origin.x += m_rect.width;
// adjust_clickable_areas(extents.width);
} else {
origin.x += m_x;
cairo::textblock block{};
block.align = m_align;
block.contents = contents;
block.fontindex = m_font;
block.bg = 0;
block.bg_rect = cairo::rect{0.0, 0.0, 0.0, 0.0};
block.bg_operator = 0;
block.x_advance = &m_blocks[m_align].x;
block.y_advance = &m_blocks[m_align].y;
if (m_bg && m_bg != m_bar.background) {
block.bg = m_bg;
block.bg_operator = m_comp_bg;
block.bg_rect.x = m_rect.x + m_blocks[m_align].x;
block.bg_rect.y = m_rect.y;
block.bg_rect.h = m_rect.height;
}
// if (m_color_background && m_color_background != m_bar.background) {
// m_context->save();
// *m_context << m_color_background;
// *m_context << m_compositing_background;
// *m_context << cairo::rect{origin.x, origin.y, extents.width, static_cast<double>(m_rect.height)};
// m_context->fill();
// m_context->restore();
// }
m_context->save();
*m_context << origin;
*m_context << m_compositing_foreground;
*m_context << m_color_foreground;
*m_context << cairo::textblock{m_alignment, contents, m_fontindex};
m_context->position(&m_x, &m_y);
*m_context << m_comp_fg;
*m_context << m_fg;
*m_context << block;
m_context->restore();
fill_underline(origin.x, m_x - origin.x);
fill_overline(origin.x, m_x - origin.x);
}
/**
* Move clickable areas position by given delta
*/
void renderer::adjust_clickable_areas(double delta) {
for (auto&& action : m_actions) {
if (!action.active && action.align == m_alignment) {
action.start_x -= delta;
action.end_x -= delta;
}
double dx = m_rect.x + m_blocks[m_align].x - origin.x;
if (dx > 0.0) {
fill_underline(origin.x, dx);
fill_overline(origin.x, dx);
}
}
/**
* Draw boxes at the location of each created action block
* Colorize the bounding box of created action blocks
*/
void renderer::highlight_clickable_areas() {
#ifdef DEBUG_HINTS
@ -452,128 +537,132 @@ void renderer::highlight_clickable_areas() {
for (auto&& action : m_actions) {
if (!action.active) {
int n = hint_num.find(action.align)->second++;
double x = action.start_x + n * DEBUG_HINTS_OFFSET_X;
double y = m_bar.pos.y + m_rect.y + n * DEBUG_HINTS_OFFSET_Y;
double x = action.start_x;
double y = m_rect.y;
double w = action.width();
double h = m_rect.height;
m_context->save();
*m_context << CAIRO_OPERATOR_OVERLAY << (n % 2 ? 0x55FF0000 : 0x5500FF00);
*m_context << CAIRO_OPERATOR_DIFFERENCE << (n % 2 ? 0xFF00FF00 : 0xFFFF0000);
*m_context << cairo::rect{x, y, w, h};
m_context->fill();
m_context->restore();
}
}
m_surface->flush();
#endif
}
bool renderer::on(const signals::ui::request_snapshot& evt) {
m_snapshot_dst = evt.cast();
return true;
}
bool renderer::on(const signals::parser::change_background& evt) {
const unsigned int color{evt.cast()};
if (color != m_color_background) {
m_color_background = color;
if (color != m_bg) {
m_log.trace_x("renderer: change_background(#%08x)", color);
m_bg = color;
}
return true;
}
bool renderer::on(const signals::parser::change_foreground& evt) {
const unsigned int color{evt.cast()};
if (color != m_color_foreground) {
m_color_foreground = color;
if (color != m_fg) {
m_log.trace_x("renderer: change_foreground(#%08x)", color);
m_fg = color;
}
return true;
}
bool renderer::on(const signals::parser::change_underline& evt) {
const unsigned int color{evt.cast()};
if (color != m_color_underline) {
m_color_underline = color;
if (color != m_ul) {
m_log.trace_x("renderer: change_underline(#%08x)", color);
m_ul = color;
}
return true;
}
bool renderer::on(const signals::parser::change_overline& evt) {
const unsigned int color{evt.cast()};
if (color != m_color_overline) {
m_color_overline = color;
if (color != m_ol) {
m_log.trace_x("renderer: change_overline(#%08x)", color);
m_ol = color;
}
return true;
}
bool renderer::on(const signals::parser::change_font& evt) {
m_fontindex = evt.cast();
const int font{evt.cast()};
if (font != m_font) {
m_log.trace_x("renderer: change_font(%i)", font);
m_font = font;
}
return true;
}
bool renderer::on(const signals::parser::change_alignment& evt) {
auto align = static_cast<const alignment&>(evt.cast());
if (align != m_alignment) {
m_alignment = align;
m_x = 0.0;
if (align != m_align) {
m_log.trace_x("renderer: change_alignment(%i)", static_cast<int>(align));
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;
}
bool renderer::on(const signals::parser::offset_pixel& evt) {
(void)evt;
// m_x += evt.cast();
m_log.trace_x("renderer: offset_pixel(%f)", evt.cast());
m_blocks[m_align].x += evt.cast();
return true;
}
bool renderer::on(const signals::parser::attribute_set& evt) {
m_attributes.set(static_cast<int>(evt.cast()), true);
m_log.trace_x("renderer: attribute_set(%i)", static_cast<int>(evt.cast()));
m_attr.set(static_cast<int>(evt.cast()), true);
return true;
}
bool renderer::on(const signals::parser::attribute_unset& evt) {
m_attributes.set(static_cast<int>(evt.cast()), false);
m_log.trace_x("renderer: attribute_unset(%i)", static_cast<int>(evt.cast()));
m_attr.set(static_cast<int>(evt.cast()), false);
return true;
}
bool renderer::on(const signals::parser::attribute_toggle& evt) {
m_attributes.flip(static_cast<int>(evt.cast()));
m_log.trace_x("renderer: attribute_toggle(%i)", static_cast<int>(evt.cast()));
m_attr.flip(static_cast<int>(evt.cast()));
return true;
}
bool renderer::on(const signals::parser::action_begin& evt) {
(void)evt;
// auto a = evt.cast();
// action_block action{};
// action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button;
// action.align = m_alignment;
// action.start_x = m_x;
// action.command = string_util::replace_all(a.command, ":", "\\:");
// action.active = true;
// m_actions.emplace_back(action);
auto a = evt.cast();
m_log.trace_x("renderer: action_begin(btn=%i, command=%s)", static_cast<int>(a.button), a.command);
action_block action{};
action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button;
action.align = m_align;
action.start_x = m_blocks.at(m_align).x;
action.command = string_util::replace_all(a.command, ":", "\\:");
action.active = true;
m_actions.emplace_back(action);
return true;
}
bool renderer::on(const signals::parser::action_end& evt) {
(void)evt;
// auto btn = evt.cast();
// int clickable_width = 0;
// for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
// if (action->active && action->align == m_alignment && action->button == btn) {
// switch (action->align) {
// case alignment::NONE:
// break;
// case alignment::LEFT:
// action->end_x = m_x;
// break;
// case alignment::CENTER:
// clickable_width = m_x - action->start_x;
// action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2;
// action->end_x = action->start_x + clickable_width;
// break;
// case alignment::RIGHT:
// action->start_x = m_rect.width - m_x + action->start_x;
// action->end_x = m_rect.width;
// break;
// }
// action->start_x += m_rect.x;
// action->end_x += m_rect.x;
// action->active = false;
// }
// }
auto btn = evt.cast();
m_log.trace_x("renderer: action_end(btn=%i)", static_cast<int>(btn));
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
if (action->active && action->align == m_align && action->button == btn) {
action->end_x = m_blocks.at(action->align).x;
action->active = false;
}
}
return true;
}

View File

@ -146,7 +146,7 @@ int main(int argc, char** argv) {
auto ctrl = controller::make(move(ipc), move(config_watch));
if (!ctrl->run(cli->has("stdout"))) {
if (!ctrl->run(cli->has("stdout"), cli->get("png"))) {
reload = true;
}
} catch (const exception& err) {