wip(refactor): Cairo drawing

This commit is contained in:
Michael Carlberg 2017-01-19 05:38:42 +01:00
parent 47a2cce03d
commit 5e1886a312
36 changed files with 1435 additions and 1262 deletions

View File

@ -52,8 +52,8 @@ flags.append('-I'+ DirectoryOfThisScript() +'/lib/concurrentqueue/include')
flags.append('-I'+ DirectoryOfThisScript() +'/lib/i3ipcpp/include') flags.append('-I'+ DirectoryOfThisScript() +'/lib/i3ipcpp/include')
flags.append('-I'+ DirectoryOfThisScript() +'/lib/xpp/include') flags.append('-I'+ DirectoryOfThisScript() +'/lib/xpp/include')
flags.append('-I'+ DirectoryOfThisScript() +'/tests') flags.append('-I'+ DirectoryOfThisScript() +'/tests')
flags.append('-I/usr/include/freetype2')
flags.append('-I/usr/include') flags.append('-I/usr/include')
flags.append('-I/usr/include/freetype2')
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory: if not working_directory:

View File

@ -56,8 +56,6 @@ if(CXXLIB_CLANG)
elseif(CXXLIB_GCC) elseif(CXXLIB_GCC)
message_colored(STATUS "Linking against libstdc++" 32) message_colored(STATUS "Linking against libstdc++" 32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
else()
message_colored(STATUS "No preferred c++lib specified... linking against system default" 33)
endif() endif()
if(ENABLE_CCACHE) if(ENABLE_CCACHE)

View File

@ -50,13 +50,13 @@ option(CXXLIB_GCC "Link against stdlibc++" OFF)
option(BUILD_IPC_MSG "Build ipc messager" ON) option(BUILD_IPC_MSG "Build ipc messager" ON)
option(BUILD_TESTS "Build testsuite" OFF) option(BUILD_TESTS "Build testsuite" OFF)
option(DEBUG_LOGGER "Enable extra debug logging" OFF)
option(DEBUG_LOGGER_TRACE "Enable verbose trace logs" OFF)
option(DEBUG_HINTS "Enable hints rendering" OFF)
option(DEBUG_WHITESPACE "Enable whitespace debug" OFF)
if(NOT DEBUG_LOGGER AND DEBUG_LOGGER_TRACE) if(DEBUG)
set(DEBUG_LOGGER_TRACE OFF) option(DEBUG_LOGGER "Debug logging" OFF)
option(DEBUG_LOGGER_VERBOSE "Debug logging (verbose)" OFF)
option(DEBUG_HINTS "Debug clickable areas" OFF)
option(DEBUG_WHITESPACE "Debug whitespace" OFF)
option(DEBUG_FONTCONFIG "Debug fontconfig" OFF)
endif() endif()
option(ENABLE_CCACHE "Enable ccache support" OFF) option(ENABLE_CCACHE "Enable ccache support" OFF)

View File

@ -11,69 +11,72 @@ function(colored_option message_level text var color_on color_off)
endif() endif()
endfunction() endfunction()
message(STATUS "--------------------------") message(STATUS " Build:")
if(CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE)
message_colored(STATUS " Build type: ${CMAKE_BUILD_TYPE}" "32;1") message_colored(STATUS " Type: ${CMAKE_BUILD_TYPE}" "37;2")
else() else()
message_colored(STATUS " Build type: NONE" "33;1") message_colored(STATUS " Type: NONE" "33;1")
endif() endif()
message(STATUS " Compiler C: ${CMAKE_C_COMPILER}")
message(STATUS " Compiler C++: ${CMAKE_CXX_COMPILER}")
message(STATUS " Compiler flags: ${CMAKE_CXX_FLAGS}")
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
message(STATUS " debug flags: ${CMAKE_CXX_FLAGS_DEBUG}")
if(NOT DEFINED ${DEBUG_LOGGER}) if(NOT DEFINED ${DEBUG_LOGGER})
set(DEBUG_LOGGER ON) set(DEBUG_LOGGER ON)
endif() endif()
if(NOT DEFINED ${ENABLE_CCACHE}) if(NOT DEFINED ${ENABLE_CCACHE})
set(ENABLE_CCACHE ON) set(ENABLE_CCACHE ON)
endif() endif()
message_colored(STATUS " CC: ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_DEBUG}" "37;2")
message_colored(STATUS " CXX: ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}" "37;2")
message_colored(STATUS " LD: ${CMAKE_LINKER} ${CMAKE_EXE_LINKER_FLAGS}${CMAKE_EXE_LINKER_FLAGS_DEBUG}" "37;2")
elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "release") elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "release")
message(STATUS " release: ${CMAKE_CXX_FLAGS_RELEASE}") message_colored(STATUS " CC: ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELEASE}" "37;2")
message_colored(STATUS " CXX: ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}" "37;2")
message_colored(STATUS " LD: ${CMAKE_LINKER} ${CMAKE_EXE_LINKER_FLAGS}${CMAKE_EXE_LINKER_FLAGS_RELEASE}" "37;2")
elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "sanitize") elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "sanitize")
message(STATUS " sanitize: ${CMAKE_CXX_FLAGS_SANITIZE}") message_colored(STATUS " CC: ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_SANITIZE}" "37;2")
message_colored(STATUS " CXX: ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_SANITIZE}" "37;2")
message_colored(STATUS " LD: ${CMAKE_LINKER} ${CMAKE_EXE_LINKER_FLAGS}${CMAKE_EXE_LINKER_FLAGS_SANITIZE}" "37;2")
elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "minsizerel") elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "minsizerel")
message(STATUS " minsizerel: ${CMAKE_CXX_FLAGS_MINSIZEREL}") message_colored(STATUS " CC: ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_MINSIZEREL}" "37;2")
message_colored(STATUS " CXX: ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}" "37;2")
message_colored(STATUS " LD: ${CMAKE_LINKER} ${CMAKE_EXE_LINKER_FLAGS}${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}" "37;2")
elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "relwithdebinfo") elseif(CMAKE_BUILD_TYPE_LOWER STREQUAL "relwithdebinfo")
message(STATUS " relwithdebinfo: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") message_colored(STATUS " CC: ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELWITHDEBINFO}" "37;2")
message_colored(STATUS " CXX: ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" "37;2")
message_colored(STATUS " LD: ${CMAKE_LINKER} ${CMAKE_EXE_LINKER_FLAGS}${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}" "37;2")
endif() endif()
if(CMAKE_EXE_LINKER_FLAGS) if(CMAKE_EXE_LINKER_FLAGS)
message(STATUS " Linker flags: ${CMAKE_EXE_LINKER_FLAGS}") message_colored(STATUS " LD: ${CMAKE_EXE_LINKER_FLAGS}" "37;2")
endif() endif()
if(CXXLIB_CLANG) message(STATUS " Targets:")
message(STATUS " C++ library: libc++") colored_option(STATUS " polybar-msg" BUILD_IPC_MSG "32;1" "37;2")
elseif(CXXLIB_GCC) colored_option(STATUS " testsuite" BUILD_TESTS "32;1" "37;2")
message(STATUS " C++ library: libstdc++")
else()
message(STATUS " C++ library: system default")
endif()
message(STATUS " Module supprt:")
colored_option(STATUS " alsa" ENABLE_ALSA "32;1" "37;2")
colored_option(STATUS " curl" ENABLE_CURL "32;1" "37;2")
colored_option(STATUS " i3" ENABLE_I3 "32;1" "37;2")
colored_option(STATUS " mpd" ENABLE_MPD "32;1" "37;2")
colored_option(STATUS " network" ENABLE_NETWORK "32;1" "37;2")
message(STATUS " X extensions:")
colored_option(STATUS " XRandR" WITH_XRANDR "32;1" "37;2")
colored_option(STATUS " XRandR (enable monitors)" ENABLE_XRANDR_MONITORS "32;1" "37;2")
colored_option(STATUS " XRender" WITH_XRENDER "32;1" "37;2")
colored_option(STATUS " XDamage" WITH_XDAMAGE "32;1" "37;2")
colored_option(STATUS " XSync" WITH_XSYNC "32;1" "37;2")
colored_option(STATUS " XComposite" WITH_XCOMPOSITE "32;1" "37;2")
colored_option(STATUS " Xkb" WITH_XKB "32;1" "37;2")
colored_option(STATUS " Xrm" WITH_XRM "32;1" "37;2")
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
message(STATUS " Debug options:")
colored_option(STATUS " Trace logging" DEBUG_LOGGER "32;1" "37;2")
colored_option(STATUS " Trace logging (verbose)" DEBUG_LOGGER_VERBOSE "32;1" "37;2")
colored_option(STATUS " Draw clickable areas" DEBUG_HINTS "32;1" "37;2")
colored_option(STATUS " Print fc-match details" DEBUG_FONTCONFIG "32;1" "37;2")
colored_option(STATUS " Enable window shading" DEBUG_SHADED "32;1" "37;2")
message(STATUS "--------------------------") message(STATUS "--------------------------")
colored_option(STATUS " Build polybar-msg ${BUILD_IPC_MSG}" BUILD_IPC_MSG "32;1" "37;2") endif()
colored_option(STATUS " Build testsuite ${BUILD_TESTS}" BUILD_TESTS "32;1" "37;2")
colored_option(STATUS " Debug logging ${DEBUG_LOGGER}" DEBUG_LOGGER "32;1" "37;2")
colored_option(STATUS " + Verbose tracing ${DEBUG_LOGGER_TRACE}" DEBUG_LOGGER_TRACE "32;1" "37;2")
colored_option(STATUS " Draw debug hints ${DEBUG_HINTS}" DEBUG_HINTS "32;1" "37;2")
colored_option(STATUS " Draw whitespace ${DEBUG_WHITESPACE}" DEBUG_WHITESPACE "32;1" "37;2")
colored_option(STATUS " Enable ccache ${ENABLE_CCACHE}" ENABLE_CCACHE "32;1" "37;2")
message(STATUS "--------------------------")
colored_option(STATUS " Enable alsa ${ENABLE_ALSA}" ENABLE_ALSA "32;1" "37;2")
colored_option(STATUS " Enable curl ${ENABLE_CURL}" ENABLE_CURL "32;1" "37;2")
colored_option(STATUS " Enable i3 ${ENABLE_I3}" ENABLE_I3 "32;1" "37;2")
colored_option(STATUS " Enable mpd ${ENABLE_MPD}" ENABLE_MPD "32;1" "37;2")
colored_option(STATUS " Enable network ${ENABLE_NETWORK}" ENABLE_NETWORK "32;1" "37;2")
message(STATUS "--------------------------")
colored_option(STATUS " XRANDR support ${WITH_XRANDR}" WITH_XRANDR "32;1" "37;2")
colored_option(STATUS " + XRandR monitors ${ENABLE_XRANDR_MONITORS}" ENABLE_XRANDR_MONITORS "32;1" "37;2")
colored_option(STATUS " XRENDER support ${WITH_XRENDER}" WITH_XRENDER "32;1" "37;2")
colored_option(STATUS " XDAMAGE support ${WITH_XDAMAGE}" WITH_XDAMAGE "32;1" "37;2")
colored_option(STATUS " XSYNC support ${WITH_XSYNC}" WITH_XSYNC "32;1" "37;2")
colored_option(STATUS " XCOMPOSITE support ${WITH_XCOMPOSITE}" WITH_XCOMPOSITE "32;1" "37;2")
colored_option(STATUS " XKB support ${WITH_XKB}" WITH_XKB "32;1" "37;2")
message(STATUS "--------------------------")
colored_option(STATUS " xcb-util-xrm ${WITH_XRM}" WITH_XRM "32;1" "37;2")
message(STATUS "--------------------------")

258
include/cairo/context.hpp Normal file
View File

@ -0,0 +1,258 @@
#pragma once
#include <cairo/cairo-xcb.h>
#include <algorithm>
#include <cmath>
#include <deque>
#include "cairo/font.hpp"
#include "cairo/surface.hpp"
#include "cairo/types.hpp"
#include "cairo/utils.hpp"
#include "common.hpp"
#include "components/logger.hpp"
#include "errors.hpp"
#include "utils/color.hpp"
#include "utils/string.hpp"
POLYBAR_NS
namespace cairo {
class context {
public:
explicit context(const surface& surface, const logger& log) : m_c(cairo_create(surface)), m_log(log) {
auto status = cairo_status(m_c);
if (status != CAIRO_STATUS_SUCCESS) {
throw application_error(sstream() << "cairo_status(): " << cairo_status_to_string(status));
}
cairo_set_antialias(m_c, CAIRO_ANTIALIAS_GOOD);
}
virtual ~context() {
cairo_destroy(m_c);
}
operator cairo_t*() const {
return m_c;
}
context& operator<<(const surface& s) {
cairo_set_source_surface(m_c, s, 0.0, 0.0);
return *this;
}
context& operator<<(cairo_operator_t o) {
cairo_set_operator(m_c, o);
return *this;
}
context& operator<<(cairo_pattern_t* s) {
cairo_set_source(m_c, s);
return *this;
}
context& operator<<(const uint32_t& c) {
// clang-format off
cairo_set_source_rgba(m_c,
color_util::red_channel<uint8_t>(c) / 255.0,
color_util::green_channel<uint8_t>(c) / 255.0,
color_util::blue_channel<uint8_t>(c) / 255.0,
color_util::alpha_channel<uint8_t>(c) / 255.0);
// clang-format on
return *this;
}
context& operator<<(const abspos& p) {
cairo_move_to(m_c, p.x, p.y);
return *this;
}
context& operator<<(const relpos& p) {
cairo_rel_move_to(m_c, p.x, p.y);
return *this;
}
context& operator<<(const rgba& f) {
cairo_set_source_rgba(m_c, f.r, f.g, f.b, f.a);
return *this;
}
context& operator<<(const rect& f) {
cairo_rectangle(m_c, f.x, f.y, f.w, f.h);
return *this;
}
context& operator<<(const line& l) {
struct line p {
l.x1, l.y1, l.x2, l.y2, l.w
};
snap(&p.x1, &p.y1);
snap(&p.x2, &p.y2);
cairo_move_to(m_c, p.x1, p.y1);
cairo_line_to(m_c, p.x2, p.y2);
cairo_set_line_width(m_c, p.w);
cairo_stroke(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);
*this << pattern;
auto stops = l.steps.size();
auto step = 1.0 / (stops - 1);
auto offset = 0.0;
for (auto&& color : l.steps) {
// clang-format off
cairo_pattern_add_color_stop_rgba(pattern, offset,
color_util::red_channel<uint8_t>(color) / 255.0,
color_util::green_channel<uint8_t>(color) / 255.0,
color_util::blue_channel<uint8_t>(color) / 255.0,
color_util::alpha_channel<uint8_t>(color) / 255.0);
// clang-format on
offset += step;
}
cairo_pattern_destroy(pattern);
}
return *this;
}
context& operator<<(const textblock& t) {
// Sort the fontlist so that the preferred font is tested first
auto& fns = m_fonts;
std::sort(fns.begin(), fns.end(), [&](const unique_ptr<font>& a, const unique_ptr<font>&) {
if (t.fontindex > 0 && std::distance(fns.begin(), std::find(fns.begin(), fns.end(), a)) == t.fontindex - 1) {
return -1;
} else {
return 0;
}
});
string utf8 = string(t.contents);
unicode_charlist chars;
utils::utf8_to_ucs4((const uint8_t*)utf8.c_str(), chars);
while (!chars.empty()) {
auto remaining = chars.size();
for (auto&& f : fns) {
auto matches = f->match(chars);
if (!matches) {
continue;
}
string subset;
auto end = chars.begin();
while (matches-- && end != chars.end()) {
subset += utf8.substr(end->offset, end->length);
end++;
}
f->use(m_c);
cairo_text_extents_t extents;
f->textwidth(utf8, &extents);
save(); // encapsulate the y pos update
*this << relpos{0.0, extents.height / 2.0 - f->extents().descent + f->offset()};
f->render(subset);
restore();
*this << relpos{extents.width, 0.0};
chars.erase(chars.begin(), end);
break;
}
if (chars.empty()) {
break;
} else if (remaining != chars.size()) {
continue;
}
char unicode[5]{'\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);
for (auto&& c : chars) {
c.offset -= chars.begin()->length;
}
chars.erase(chars.begin(), ++chars.begin());
}
return *this;
}
context& operator<<(unique_ptr<font>&& f) {
m_fonts.emplace_back(forward<decltype(f)>(f));
return *this;
}
context& save(bool save_point = true) {
if (save_point) {
m_points.emplace_front(make_pair<double, double>(0.0, 0.0));
position(&m_points.front().first, &m_points.front().second);
}
cairo_save(m_c);
return *this;
}
context& restore(bool restore_point = true) {
if (restore_point) {
*this << abspos{m_points.front().first, m_points.front().first};
m_points.pop_front();
}
cairo_restore(m_c);
return *this;
}
context& paint() {
cairo_paint(m_c);
return *this;
}
context& paint(double alpha) {
cairo_paint_with_alpha(m_c, alpha);
return *this;
}
context& fill() {
cairo_fill(m_c);
return *this;
}
context& clip(const rect& r) {
*this << r;
cairo_clip(m_c);
return *this;
}
context& reset_clip() {
cairo_reset_clip(m_c);
return *this;
}
context& position(double* x, double* y) {
*x = 0.0;
*y = 0.0;
if (cairo_has_current_point(m_c)) {
cairo_get_current_point(m_c, x, y);
}
return *this;
}
context& snap(double* x, double* y) {
cairo_user_to_device(m_c, x, y);
*x = ((int)*x + 0.5);
*y = ((int)*y + 0.5);
return *this;
}
protected:
cairo_t* m_c;
const logger& m_log;
vector<unique_ptr<font>> m_fonts;
std::deque<pair<double, double>> m_points;
};
}
POLYBAR_NS_END

283
include/cairo/font.hpp Normal file
View File

@ -0,0 +1,283 @@
#pragma once
#include <cairo/cairo-ft.h>
#include <list>
#include <set>
#include <string>
#include "common.hpp"
#include "errors.hpp"
#include "settings.hpp"
#include "utils/math.hpp"
#include "utils/scope.hpp"
#include "utils/string.hpp"
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
*/
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>;
/**
* @brief Abstract font face
*/
class font {
public:
explicit font(cairo_t* cairo, double offset) : m_cairo(cairo), m_offset(offset) {}
virtual ~font() {};
virtual string name() const = 0;
virtual string file() const = 0;
virtual double offset() const = 0;
virtual double size() const = 0;
virtual void use(cairo_t* c) {
cairo_set_font_face(c, cairo_font_face_reference(m_font_face));
}
virtual size_t match(unicode_charlist& charlist) = 0;
virtual size_t render(const string& text) = 0;
virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0;
cairo_font_extents_t extents() const {
return m_extents;
}
protected:
cairo_t* m_cairo;
cairo_font_face_t* m_font_face{nullptr};
cairo_font_extents_t m_extents{};
double m_offset{0.0};
};
/**
* @brief Font based on fontconfig/freetype
*/
class font_fc : public font {
public:
explicit font_fc(cairo_t* cairo, FcPattern* pattern, double offset) : font(cairo, offset), m_pattern(pattern) {
cairo_matrix_t fm;
cairo_matrix_t ctm;
cairo_matrix_init_scale(&fm, size(), size());
cairo_get_matrix(m_cairo, &ctm);
auto fontface = cairo_ft_font_face_create_for_pattern(m_pattern);
auto opts = cairo_font_options_create();
m_scaled = cairo_scaled_font_create(fontface, &fm, &ctm, opts);
cairo_font_options_destroy(opts);
cairo_font_face_destroy(fontface);
auto status = cairo_scaled_font_status(m_scaled);
if (status != CAIRO_STATUS_SUCCESS) {
throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status));
}
auto lock = make_unique<details::ft_face_lock>(cairo_scaled_font_reference(m_scaled));
auto face = static_cast<FT_Face>(*lock);
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) {
return;
} else if (FT_Select_Charmap(face, FT_ENCODING_BIG5) == FT_Err_Ok) {
return;
} else if (FT_Select_Charmap(face, FT_ENCODING_SJIS) == FT_Err_Ok) {
return;
}
}
~font_fc() override {
if (m_scaled != nullptr) {
cairo_scaled_font_destroy(m_scaled);
}
if (m_pattern != nullptr) {
FcPatternDestroy(m_pattern);
}
}
string name() const override {
return property("family");
}
string file() const override {
return property("file");
}
double offset() const override {
return m_offset;
}
double size() const override {
bool scalable;
double px;
property(FC_SCALABLE, &scalable);
if (scalable) {
property(FC_SIZE, &px);
} else {
property(FC_PIXEL_SIZE, &px);
px = static_cast<int>(px + 0.5);
}
return px;
}
void use(cairo_t* c) override {
cairo_set_scaled_font(c, cairo_scaled_font_reference(m_scaled));
}
size_t match(unicode_charlist& charlist) override {
auto lock = make_unique<details::ft_face_lock>(cairo_scaled_font_reference(m_scaled));
auto face = static_cast<FT_Face>(*lock);
size_t available_chars = 0;
for (auto&& c : charlist) {
if (FT_Get_Char_Index(face, c.codepoint)) {
available_chars++;
} else {
break;
}
}
return available_chars;
}
size_t render(const string& text) override {
double x, y;
cairo_get_current_point(m_cairo, &x, &y);
cairo_glyph_t* glyphs{nullptr};
cairo_text_cluster_t* clusters{nullptr};
cairo_text_cluster_flags_t cf{};
int nglyphs = 0, nclusters = 0;
string utf8 = string(text);
auto status = cairo_scaled_font_text_to_glyphs(cairo_scaled_font_reference(m_scaled), x, y, utf8.c_str(),
utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf);
if (status != CAIRO_STATUS_SUCCESS) {
throw application_error(sstream() << "cairo_scaled_font_status()" << cairo_status_to_string(status));
}
size_t bytes = 0;
for (int g = 0; g < nglyphs; g++) {
if (glyphs[g].index) {
bytes += clusters[g].num_bytes;
} else {
break;
}
}
if (bytes) {
utf8 = text.substr(0, bytes);
cairo_scaled_font_text_to_glyphs(
m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf);
cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf);
}
return bytes;
}
void textwidth(const string& text, cairo_text_extents_t* extents) override {
cairo_scaled_font_text_extents(cairo_scaled_font_reference(m_scaled), text.c_str(), extents);
}
protected:
string property(string&& property) const {
FcChar8* file;
if (FcPatternGetString(m_pattern, property.c_str(), 0, &file) == FcResultMatch) {
return string(reinterpret_cast<char*>(file));
} else {
return "";
}
}
void property(string&& property, bool* dst) const {
FcBool b;
FcPatternGetBool(m_pattern, property.c_str(), 0, &b);
*dst = b;
}
void property(string&& property, double* dst) const {
FcPatternGetDouble(m_pattern, property.c_str(), 0, dst);
}
void property(string&& property, int* dst) const {
FcPatternGetInteger(m_pattern, property.c_str(), 0, dst);
}
private:
cairo_scaled_font_t* m_scaled{nullptr};
FcPattern* m_pattern{nullptr};
};
/**
* Match and create font from given fontconfig pattern
*/
decltype(auto) make_font(cairo_t* cairo, string&& fontname, double offset) {
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) {
throw application_error("Could not load FreeType");
}
static auto fc_cleanup = scope_util::make_exit_handler([] {
FT_Done_FreeType(details::g_ftlib);
FcFini();
});
auto pattern = FcNameParse((FcChar8*)fontname.c_str());
FcDefaultSubstitute(pattern);
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcResult result;
FcPattern* match = FcFontMatch(nullptr, pattern, &result);
FcPatternDestroy(pattern);
if (match == nullptr) {
throw application_error("Could not load font \"" + fontname + "\"");
}
#ifdef DEBUG_FONTCONFIG
FcPatternPrint(match);
#endif
return make_unique<font_fc>(cairo, match, offset);
}
}
POLYBAR_NS_END

15
include/cairo/fwd.hpp Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include "common.hpp"
POLYBAR_NS
namespace cairo {
class context;
class surface;
class xcb_surface;
class font;
class font_fc;
}
POLYBAR_NS_END

50
include/cairo/surface.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include <cairo/cairo-xcb.h>
#include "cairo/types.hpp"
#include "common.hpp"
#include "utils/color.hpp"
POLYBAR_NS
namespace cairo {
class surface {
public:
explicit surface(cairo_surface_t* s) : m_s(s) {}
virtual ~surface() {
cairo_surface_destroy(m_s);
}
operator cairo_surface_t*() const {
return m_s;
}
surface& flush() {
cairo_surface_flush(m_s);
return *this;
}
surface& dirty() {
cairo_surface_mark_dirty(m_s);
return *this;
}
surface& dirty(const rect& r) {
cairo_surface_mark_dirty_rectangle(m_s, r.x, r.y, r.w, r.h);
return *this;
}
protected:
cairo_surface_t* m_s;
};
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 {}
};
}
POLYBAR_NS_END

46
include/cairo/types.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "common.hpp"
POLYBAR_NS
namespace cairo {
struct abspos {
double x;
double y;
};
struct relpos {
double x;
double y;
};
struct rect {
double x;
double y;
double w;
double h;
};
struct line {
double x1;
double y1;
double x2;
double y2;
double w;
};
struct linear_gradient {
double x0;
double y0;
double x1;
double y1;
vector<uint32_t> steps;
};
struct textblock {
string contents;
uint8_t fontindex;
};
}
POLYBAR_NS_END

142
include/cairo/utils.hpp Normal file
View File

@ -0,0 +1,142 @@
#pragma once
#include <cairo/cairo.h>
#include <map>
#include "cairo/font.hpp"
#include "common.hpp"
#include "utils/string.hpp"
POLYBAR_NS
namespace cairo {
namespace utils {
/**
* @see <cairo/cairo.h>
*/
cairo_operator_t str2operator(const string& mode, cairo_operator_t fallback) {
if (mode.empty()) {
return fallback;
}
static std::map<string, cairo_operator_t> modes;
if (modes.empty()) {
modes["clear"s] = CAIRO_OPERATOR_CLEAR;
modes["source"s] = CAIRO_OPERATOR_SOURCE;
modes["over"s] = CAIRO_OPERATOR_OVER;
modes["in"s] = CAIRO_OPERATOR_IN;
modes["out"s] = CAIRO_OPERATOR_OUT;
modes["atop"s] = CAIRO_OPERATOR_ATOP;
modes["dest"s] = CAIRO_OPERATOR_DEST;
modes["dest-over"s] = CAIRO_OPERATOR_DEST_OVER;
modes["dest-in"s] = CAIRO_OPERATOR_DEST_IN;
modes["dest-out"s] = CAIRO_OPERATOR_DEST_OUT;
modes["dest-atop"s] = CAIRO_OPERATOR_DEST_ATOP;
modes["xor"s] = CAIRO_OPERATOR_XOR;
modes["add"s] = CAIRO_OPERATOR_ADD;
modes["saturate"s] = CAIRO_OPERATOR_SATURATE;
modes["multiply"s] = CAIRO_OPERATOR_MULTIPLY;
modes["screen"s] = CAIRO_OPERATOR_SCREEN;
modes["overlay"s] = CAIRO_OPERATOR_OVERLAY;
modes["darken"s] = CAIRO_OPERATOR_DARKEN;
modes["lighten"s] = CAIRO_OPERATOR_LIGHTEN;
modes["color-dodge"s] = CAIRO_OPERATOR_COLOR_DODGE;
modes["color-burn"s] = CAIRO_OPERATOR_COLOR_BURN;
modes["hard-light"s] = CAIRO_OPERATOR_HARD_LIGHT;
modes["soft-light"s] = CAIRO_OPERATOR_SOFT_LIGHT;
modes["difference"s] = CAIRO_OPERATOR_DIFFERENCE;
modes["exclusion"s] = CAIRO_OPERATOR_EXCLUSION;
modes["hsl-hue"s] = CAIRO_OPERATOR_HSL_HUE;
modes["hsl-saturation"s] = CAIRO_OPERATOR_HSL_SATURATION;
modes["hsl-color"s] = CAIRO_OPERATOR_HSL_COLOR;
modes["hsl-luminosity"s] = CAIRO_OPERATOR_HSL_LUMINOSITY;
}
auto it = modes.find(mode);
return it != modes.end() ? it->second : fallback;
}
/**
* @brief Create a UCS-4 codepoint from a utf-8 encoded string
*/
bool utf8_to_ucs4(const unsigned char* src, unicode_charlist& result_list) {
if (!src) {
return false;
}
const unsigned char* first = src;
while (*first) {
int len = 0;
unsigned long result = 0;
if ((*first >> 7) == 0) {
len = 1;
result = *first;
} else if ((*first >> 5) == 6) {
len = 2;
result = *first & 31;
} else if ((*first >> 4) == 14) {
len = 3;
result = *first & 15;
} else if ((*first >> 3) == 30) {
len = 4;
result = *first & 7;
} else {
return false;
}
const unsigned char* next;
for (next = first + 1; *next && ((*next >> 6) == 2) && (next - first < len); next++) {
result = result << 6;
result |= *next & 63;
}
unicode_character uc_char;
uc_char.codepoint = result;
uc_char.offset = first - src;
uc_char.length = next - first;
result_list.push_back(uc_char);
first = next;
}
return true;
}
/**
* @brief Convert a UCS-4 codepoint to a utf-8 encoded string
*/
size_t ucs4_to_utf8(char* utf8, uint32_t ucs) {
if (ucs <= 0x7f) {
*utf8 = ucs;
return 1;
} else if (ucs <= 0x07ff) {
*(utf8++) = ((ucs >> 6) & 0xff) | 0xc0;
*utf8 = (ucs & 0x3f) | 0x80;
return 2;
} else if (ucs <= 0xffff) {
*(utf8++) = ((ucs >> 12) & 0x0f) | 0xe0;
*(utf8++) = ((ucs >> 6) & 0x3f) | 0x80;
*utf8 = (ucs & 0x3f) | 0x80;
return 3;
} else if (ucs <= 0x1fffff) {
*(utf8++) = ((ucs >> 18) & 0x07) | 0xf0;
*(utf8++) = ((ucs >> 12) & 0x3f) | 0x80;
*(utf8++) = ((ucs >> 6) & 0x3f) | 0x80;
*utf8 = (ucs & 0x3f) | 0x80;
return 4;
} else if (ucs <= 0x03ffffff) {
*(utf8++) = ((ucs >> 24) & 0x03) | 0xf8;
*(utf8++) = ((ucs >> 18) & 0x3f) | 0x80;
*(utf8++) = ((ucs >> 12) & 0x3f) | 0x80;
*(utf8++) = ((ucs >> 6) & 0x3f) | 0x80;
*utf8 = (ucs & 0x3f) | 0x80;
return 5;
} else if (ucs <= 0x7fffffff) {
*(utf8++) = ((ucs >> 30) & 0x01) | 0xfc;
*(utf8++) = ((ucs >> 24) & 0x3f) | 0x80;
*(utf8++) = ((ucs >> 18) & 0x3f) | 0x80;
*(utf8++) = ((ucs >> 12) & 0x3f) | 0x80;
*(utf8++) = ((ucs >> 6) & 0x3f) | 0x80;
*utf8 = (ucs & 0x3f) | 0x80;
return 6;
} else {
return 0;
}
}
}
}
POLYBAR_NS_END

View File

@ -4,11 +4,11 @@
#include <mutex> #include <mutex>
#include "common.hpp" #include "common.hpp"
#include "settings.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "events/signal_fwd.hpp" #include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp" #include "events/signal_receiver.hpp"
#include "settings.hpp"
#include "x11/events.hpp" #include "x11/events.hpp"
#include "x11/types.hpp" #include "x11/types.hpp"
#include "x11/window.hpp" #include "x11/window.hpp"
@ -22,15 +22,14 @@ class logger;
class parser; class parser;
class renderer; class renderer;
class screen; class screen;
class signal_emitter;
class taskqueue; class taskqueue;
class tray_manager; class tray_manager;
// }}} // }}}
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify, class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify,
evt::leave_notify, evt::destroy_notify, evt::client_message>, evt::leave_notify, evt::destroy_notify, evt::client_message>,
public signal_receiver<SIGN_PRIORITY_BAR, signals::eventqueue::start, signals::ui::tick, signals::ui::shade_window, signals::ui::unshade_window, public signal_receiver<SIGN_PRIORITY_BAR, signals::eventqueue::start, signals::ui::tick,
signals::ui::dim_window> { signals::ui::shade_window, signals::ui::unshade_window, signals::ui::dim_window> {
public: public:
using make_type = unique_ptr<bar>; using make_type = unique_ptr<bar>;
static make_type make(bool only_initialize_values = false); static make_type make(bool only_initialize_values = false);

View File

@ -36,7 +36,7 @@ using modulemap_t = std::map<alignment, vector<module_t>>;
class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eventqueue::exit_terminate, signals::eventqueue::exit_reload, class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eventqueue::exit_terminate, signals::eventqueue::exit_reload,
signals::eventqueue::notify_change, signals::eventqueue::notify_forcechange, signals::eventqueue::check_state, signals::ipc::action, signals::eventqueue::notify_change, signals::eventqueue::notify_forcechange, signals::eventqueue::check_state, signals::ipc::action,
signals::ipc::command, signals::ipc::hook, signals::ui::button_press> { signals::ipc::command, signals::ipc::hook, signals::ui::ready, signals::ui::button_press> {
public: public:
using make_type = unique_ptr<controller>; using make_type = unique_ptr<controller>;
static make_type make(unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& config_watch); static make_type make(unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& config_watch);
@ -61,6 +61,7 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
bool on(const signals::eventqueue::exit_terminate& evt); bool on(const signals::eventqueue::exit_terminate& evt);
bool on(const signals::eventqueue::exit_reload& evt); bool on(const signals::eventqueue::exit_reload& evt);
bool on(const signals::eventqueue::check_state& evt); bool on(const signals::eventqueue::check_state& evt);
bool on(const signals::ui::ready& evt);
bool on(const signals::ui::button_press& evt); bool on(const signals::ui::button_press& evt);
bool on(const signals::ipc::action& evt); bool on(const signals::ipc::action& evt);
bool on(const signals::ipc::command& evt); bool on(const signals::ipc::command& evt);
@ -78,6 +79,11 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
array<unique_ptr<file_descriptor>, 2> m_queuefd{}; array<unique_ptr<file_descriptor>, 2> m_queuefd{};
/**
* @brief State flag
*/
std::atomic<bool> m_process_events{false};
/** /**
* @brief Controls weather the output gets printed to stdout * @brief Controls weather the output gets printed to stdout
*/ */

View File

@ -41,7 +41,7 @@ class logger {
void trace(string message, Args... args) const { void trace(string message, Args... args) const {
output(loglevel::TRACE, message, args...); output(loglevel::TRACE, message, args...);
} }
#ifdef DEBUG_LOGGER_TRACE #ifdef DEBUG_LOGGER_VERBOSE
template <typename... Args> template <typename... Args>
void trace_x(string message, Args... args) const { void trace_x(string message, Args... args) const {
output(loglevel::TRACE, message, args...); output(loglevel::TRACE, message, args...);

View File

@ -17,10 +17,6 @@ DEFINE_CHILD_ERROR(unclosed_actionblocks, parser_error);
class parser { class parser {
public: public:
struct packet {
uint16_t data[128]{0U};
size_t length{0};
};
using make_type = unique_ptr<parser>; using make_type = unique_ptr<parser>;
static make_type make(); static make_type make();

View File

@ -1,94 +1,71 @@
#pragma once #pragma once
#include <cairo/cairo.h>
#include <bitset>
#include "cairo/fwd.hpp"
#include "common.hpp" #include "common.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "events/signal_fwd.hpp" #include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp" #include "events/signal_receiver.hpp"
#include "x11/extensions/fwd.hpp" #include "x11/extensions/fwd.hpp"
#include "x11/fonts.hpp"
#include "x11/types.hpp" #include "x11/types.hpp"
POLYBAR_NS POLYBAR_NS
// fwd // fwd {{{
class connection; class connection;
class font_manager; class config;
class logger; class logger;
class signal_emitter; // }}}
using namespace signals::parser;
using std::map; using std::map;
class renderer class renderer
: public signal_receiver<SIGN_PRIORITY_RENDERER, change_background, change_foreground, change_underline, : public signal_receiver<SIGN_PRIORITY_RENDERER, signals::parser::change_background,
change_overline, change_font, change_alignment, offset_pixel, attribute_set, attribute_unset, signals::parser::change_foreground, signals::parser::change_underline, signals::parser::change_overline,
attribute_toggle, action_begin, action_end, write_text_ascii, write_text_unicode, write_text_string> { signals::parser::change_font, signals::parser::change_alignment, signals::parser::offset_pixel,
signals::parser::attribute_set, signals::parser::attribute_unset, signals::parser::attribute_toggle,
signals::parser::action_begin, signals::parser::action_end, signals::parser::text> {
public: public:
enum class gc : uint8_t { BG, FG, OL, UL, BT, BB, BL, BR };
using make_type = unique_ptr<renderer>; using make_type = unique_ptr<renderer>;
static make_type make(const bar_settings& bar, vector<string>&& fonts); static make_type make(const bar_settings& bar);
explicit renderer(connection& conn, signal_emitter& emitter, const logger& logger, explicit renderer(
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts); connection& conn, signal_emitter& emitter, const config&, const logger& logger, const bar_settings& bar);
~renderer(); ~renderer();
renderer(const renderer& o) = delete;
renderer& operator=(const renderer& o) = delete;
xcb_window_t window() const; xcb_window_t window() const;
const vector<action_block> actions() const;
void begin(); void begin();
void end(); void end();
void flush(bool clear); void flush();
void reserve_space(edge side, uint16_t w); void reserve_space(edge side, uint16_t w);
void set_background(const uint32_t color);
void set_foreground(const uint32_t color);
void set_underline(const uint32_t color);
void set_overline(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 toggle_attribute(const attribute attr);
bool check_attribute(const attribute attr);
void fill_background(); void fill_background();
void fill_overline(int16_t x, uint16_t w); void fill_overline(double x, double w);
void fill_underline(int16_t x, uint16_t w); void fill_underline(double x, double w);
void fill_shift(const int16_t px); void fill_borders();
void draw_text(const string& contents);
void draw_textstring(const uint16_t* text, size_t len);
void begin_action(const mousebtn btn, const string& cmd);
void end_action(const mousebtn btn);
const vector<action_block> get_actions();
protected: protected:
int16_t shift_content(int16_t x, const int16_t shift_x); void adjust_clickable_areas(double width);
int16_t shift_content(const int16_t shift_x); void highlight_clickable_areas();
bool on(const change_background& evt); bool on(const signals::parser::change_background& evt);
bool on(const change_foreground& evt); bool on(const signals::parser::change_foreground& evt);
bool on(const change_underline& evt); bool on(const signals::parser::change_underline& evt);
bool on(const change_overline& evt); bool on(const signals::parser::change_overline& evt);
bool on(const change_font& evt); bool on(const signals::parser::change_font& evt);
bool on(const change_alignment& evt); bool on(const signals::parser::change_alignment& evt);
bool on(const offset_pixel& evt); bool on(const signals::parser::offset_pixel& evt);
bool on(const attribute_set& evt); bool on(const signals::parser::attribute_set& evt);
bool on(const attribute_unset& evt); bool on(const signals::parser::attribute_unset& evt);
bool on(const attribute_toggle& evt); bool on(const signals::parser::attribute_toggle& evt);
bool on(const action_begin& evt); bool on(const signals::parser::action_begin& evt);
bool on(const action_end& evt); bool on(const signals::parser::action_end& evt);
bool on(const write_text_ascii& evt); bool on(const signals::parser::text& evt);
bool on(const write_text_unicode& evt);
bool on(const write_text_string& evt);
#ifdef DEBUG_HINTS
vector<xcb_window_t> m_debughints;
void debug_hints();
#endif
protected: protected:
struct reserve_area { struct reserve_area {
@ -99,34 +76,45 @@ class renderer
private: private:
connection& m_connection; connection& m_connection;
signal_emitter& m_sig; signal_emitter& m_sig;
const config& m_conf;
const logger& m_log; const logger& m_log;
unique_ptr<font_manager> m_fontmanager;
const bar_settings& m_bar; const bar_settings& m_bar;
xcb_rectangle_t m_rect{0, 0, 0U, 0U}; xcb_rectangle_t m_rect{0, 0, 0U, 0U};
xcb_rectangle_t m_cleared{0, 0, 0U, 0U};
reserve_area m_cleararea{}; reserve_area m_cleararea{};
uint8_t m_depth{32}; uint8_t m_depth{32};
xcb_window_t m_window; xcb_window_t m_window;
xcb_colormap_t m_colormap; xcb_colormap_t m_colormap;
xcb_visualtype_t* m_visual; xcb_visualtype_t* m_visual;
// xcb_gcontext_t m_gcontext; xcb_gcontext_t m_gcontext;
xcb_pixmap_t m_pixmap; xcb_pixmap_t m_pixmap;
map<gc, xcb_gcontext_t> m_gcontexts;
map<alignment, xcb_pixmap_t> m_pixmaps;
vector<action_block> m_actions; vector<action_block> m_actions;
// bool m_autosize{false}; // bool m_autosize{false};
uint16_t m_currentx{0U};
alignment m_alignment{alignment::NONE};
map<gc, uint32_t> m_colors;
uint8_t m_attributes{0U};
uint8_t m_fontindex{0};
xcb_font_t m_gcfont{XCB_NONE}; unique_ptr<cairo::context> m_context;
unique_ptr<cairo::surface> m_surface;
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;
alignment m_alignment{alignment::NONE};
std::bitset<2> m_attributes;
uint8_t m_fontindex;
uint32_t m_color_background;
uint32_t m_color_foreground;
uint32_t m_color_overline;
uint32_t m_color_underline;
double m_x{0.0};
double m_y{0.0};
}; };
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -9,9 +9,10 @@
POLYBAR_NS POLYBAR_NS
// fwd // fwd {{{
struct randr_output; struct randr_output;
using monitor_t = shared_ptr<randr_output>; using monitor_t = shared_ptr<randr_output>;
// }}}
struct enum_hash { struct enum_hash {
template <typename T> template <typename T>
@ -135,8 +136,9 @@ struct bar_settings {
side_values module_margin{0U, 0U}; side_values module_margin{0U, 0U};
edge_values strut{0U, 0U, 0U, 0U}; edge_values strut{0U, 0U, 0U, 0U};
uint32_t background{0xFFFFFFFF}; uint32_t background{0xFF000000};
uint32_t foreground{0xFF000000}; uint32_t foreground{0xFFFFFFFF};
vector<uint32_t> background_steps;
line_settings underline{}; line_settings underline{};
line_settings overline{}; line_settings overline{};

View File

@ -90,6 +90,9 @@ namespace signals {
} }
namespace ui { namespace ui {
struct ready : public detail::base_signal<ready> {
using base_type::base_type;
};
struct tick : public detail::base_signal<tick> { struct tick : public detail::base_signal<tick> {
using base_type::base_type; using base_type::base_type;
}; };
@ -153,13 +156,7 @@ namespace signals {
struct action_end : public detail::value_signal<action_end, mousebtn> { struct action_end : public detail::value_signal<action_end, mousebtn> {
using base_type::base_type; using base_type::base_type;
}; };
struct write_text_ascii : public detail::value_signal<write_text_ascii, uint16_t> { struct text : public detail::value_signal<text, string> {
using base_type::base_type;
};
struct write_text_unicode : public detail::value_signal<write_text_unicode, uint16_t> {
using base_type::base_type;
};
struct write_text_string : public detail::value_signal<write_text_string, polybar::parser::packet> {
using base_type::base_type; using base_type::base_type;
}; };
} }

View File

@ -28,6 +28,7 @@ namespace signals {
struct action; struct action;
} }
namespace ui { namespace ui {
struct ready;
struct tick; struct tick;
struct button_press; struct button_press;
struct visibility_change; struct visibility_change;
@ -51,9 +52,7 @@ namespace signals {
struct attribute_toggle; struct attribute_toggle;
struct action_begin; struct action_begin;
struct action_end; struct action_end;
struct write_text_ascii; struct text;
struct write_text_unicode;
struct write_text_string;
} }
} }

View File

@ -23,6 +23,9 @@ namespace {
inline bool operator==(uint8_t id, event_type type) { inline bool operator==(uint8_t id, event_type type) {
return id == static_cast<uint8_t>(type); return id == static_cast<uint8_t>(type);
} }
inline bool operator!=(uint8_t id, event_type type) {
return !(id == static_cast<uint8_t>(type));
}
/** /**
* Create QUIT event * Create QUIT event

View File

@ -41,10 +41,14 @@
#cmakedefine XPP_EXTENSION_LIST @XPP_EXTENSION_LIST@ #cmakedefine XPP_EXTENSION_LIST @XPP_EXTENSION_LIST@
#if DEBUG
#cmakedefine DEBUG_LOGGER #cmakedefine DEBUG_LOGGER
#cmakedefine DEBUG_LOGGER_TRACE #cmakedefine DEBUG_LOGGER_VERBOSE
#cmakedefine DEBUG_HINTS #cmakedefine DEBUG_HINTS
#cmakedefine DEBUG_WHITESPACE #cmakedefine DEBUG_WHITESPACE
#cmakedefine DEBUG_SHADED
#cmakedefine DEBUG_FONTCONFIG
#endif
static const size_t EVENT_SIZE = 64; static const size_t EVENT_SIZE = 64;

View File

@ -13,6 +13,11 @@ class cache {
using map_type = std::unordered_map<KeyType, std::weak_ptr<ValueType>>; using map_type = std::unordered_map<KeyType, std::weak_ptr<ValueType>>;
using safe_map_type = mutex_wrapper<map_type>; using safe_map_type = mutex_wrapper<map_type>;
bool check(const KeyType& key) {
std::lock_guard<safe_map_type> guard(m_cache);
return m_cache.find(key) == m_cache.end();
}
template <typename... MakeArgs> template <typename... MakeArgs>
shared_ptr<ValueType> object(const KeyType& key, MakeArgs&&... make_args) { shared_ptr<ValueType> object(const KeyType& key, MakeArgs&&... make_args) {
std::lock_guard<safe_map_type> guard(m_cache); std::lock_guard<safe_map_type> guard(m_cache);
@ -27,6 +32,4 @@ class cache {
safe_map_type m_cache; safe_map_type m_cache;
}; };
namespace cache_util {}
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -1,12 +1,15 @@
#pragma once #pragma once
#include <iomanip>
#include "common.hpp" #include "common.hpp"
#include "utils/string.hpp" #include "utils/cache.hpp"
POLYBAR_NS POLYBAR_NS
static cache<string, uint32_t> g_cache_hex;
static cache<uint32_t, string> g_cache_colors;
struct rgba;
namespace color_util { namespace color_util {
template <typename T = uint8_t> template <typename T = uint8_t>
T alpha_channel(const uint32_t value) { T alpha_channel(const uint32_t value) {
@ -55,6 +58,9 @@ namespace color_util {
template <typename T> template <typename T>
string hex(uint32_t color) { string hex(uint32_t color) {
string hex;
if (!g_cache_hex.check(color)) {
char s[12]; char s[12];
size_t len = 0; size_t len = 0;
@ -69,7 +75,10 @@ namespace color_util {
len = snprintf(s, sizeof(s), "#%02x%02x%02x", r, g, b); len = snprintf(s, sizeof(s), "#%02x%02x%02x", r, g, b);
} }
return string{s, 0, len}; hex = string(s, len);
}
return *g_cache_hex.object(color, hex);
} }
inline string parse_hex(string hex) { inline string parse_hex(string hex) {
@ -107,4 +116,43 @@ namespace color_util {
} }
} }
struct rgb {
double r;
double g;
double b;
// clang-format off
explicit rgb(double r, double g, double b) : r(r), g(g), b(b) {}
explicit rgb(uint32_t color) : rgb(
color_util::red_channel<uint8_t>(color_util::premultiply_alpha(color) / 255.0),
color_util::green_channel<uint8_t>(color_util::premultiply_alpha(color) / 255.0),
color_util::blue_channel<uint8_t>(color_util::premultiply_alpha(color) / 255.0)) {}
// clang-format on
};
struct rgba {
double r;
double g;
double b;
double a;
// clang-format off
explicit rgba(double r, double g, double b, double a) : r(r), g(g), b(b), a(a) {}
explicit rgba(uint32_t color) : rgba(
color_util::red_channel<uint8_t>(color) / 255.0,
color_util::green_channel<uint8_t>(color) / 255.0,
color_util::blue_channel<uint8_t>(color) / 255.0,
color_util::alpha_channel<uint8_t>(color) / 255.0) {}
// clang-format on
operator uint32_t() {
// clang-format off
return static_cast<int>(a * 255) << 24
| static_cast<int>(r * 255) << 16
| static_cast<int>(g * 255) << 8
| static_cast<int>(b * 255);
// clang-format on
}
};
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -87,6 +87,10 @@ namespace math_util {
ReturnType nearest_5(double value) { ReturnType nearest_5(double value) {
return static_cast<ReturnType>(static_cast<int>(value / 5.0 + 0.5) * 5.0); return static_cast<ReturnType>(static_cast<int>(value / 5.0 + 0.5) * 5.0);
} }
inline int ceil(double value, int step = 1) {
return static_cast<int>((value * 10 + step * 10 - 1) / (step * 10)) * step;
}
} }
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -1,41 +0,0 @@
#pragma once
#include <map>
#include <X11/Xft/Xft.h>
#include <unordered_map>
#include "common.hpp"
#include "utils/color.hpp"
#include "utils/concurrency.hpp"
POLYBAR_NS
class color {
public:
explicit color(string hex);
string source() const;
operator XRenderColor() const;
operator string() const;
explicit operator uint32_t();
operator uint32_t() const;
static const color& parse(string input, const color& fallback);
static const color& parse(string input);
protected:
uint32_t m_value;
uint32_t m_color;
string m_source;
};
extern mutex_wrapper<std::unordered_map<string, color>> g_colorstore;
extern const color& g_colorempty;
extern const color& g_colorblack;
extern const color& g_colorwhite;
POLYBAR_NS_END

View File

@ -1,95 +0,0 @@
#pragma once
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_GLYPH_H
#include <X11/Xft/Xft.h>
#include <xcb/xcbext.h>
#include <unordered_map>
#include "common.hpp"
#include "x11/color.hpp"
#include "x11/types.hpp"
POLYBAR_NS
using std::map;
using std::unordered_map;
// fwd
class connection;
class logger;
struct font_ref {
explicit font_ref() = default;
font_ref(const font_ref& o) = delete;
font_ref& operator=(const font_ref& o) = delete;
XftFont* xft{nullptr};
xcb_font_t ptr{XCB_NONE};
int offset_y{0};
int ascent{0};
int descent{0};
int height{0};
int width{0};
uint16_t char_max{0};
uint16_t char_min{0};
vector<xcb_charinfo_t> width_lut{};
unordered_map<uint16_t, wchar_t> glyph_widths{};
static struct _deleter { void operator()(font_ref* font); } deleter;
};
class font_manager {
public:
using make_type = unique_ptr<font_manager>;
static make_type make();
explicit font_manager(connection& conn, const logger& logger);
~font_manager();
font_manager(const font_manager& o) = delete;
font_manager& operator=(const font_manager& o) = delete;
void set_visual(Visual* v);
void cleanup();
bool load(const string& name, uint8_t fontindex = 0, int8_t offset_y = 0);
void fontindex(uint8_t index);
shared_ptr<font_ref> match_char(const uint16_t chr);
uint8_t glyph_width(const shared_ptr<font_ref>& font, const uint16_t chr);
void drawtext(const shared_ptr<font_ref>& font, xcb_pixmap_t pm, xcb_gcontext_t gc, int16_t x, int16_t y,
const uint16_t* chars, size_t num_chars);
void allocate_color(uint32_t color);
void allocate_color(XRenderColor color);
protected:
bool open_xcb_font(const shared_ptr<font_ref>& font, string fontname);
uint8_t glyph_width_xft(const shared_ptr<font_ref>& font, const uint16_t chr);
uint8_t glyph_width_xcb(const shared_ptr<font_ref>& font, const uint16_t chr);
bool has_glyph_xft(const shared_ptr<font_ref>& font, const uint16_t chr);
bool has_glyph_xcb(const shared_ptr<font_ref>& font, const uint16_t chr);
void xcb_poly_text_16(xcb_drawable_t d, xcb_gcontext_t gc, int16_t x, int16_t y, uint8_t len, uint16_t* str);
private:
connection& m_connection;
const logger& m_logger;
Display* m_display{nullptr};
Visual* m_visual{nullptr};
Colormap m_colormap;
map<uint8_t, shared_ptr<font_ref>> m_fonts{};
uint8_t m_fontindex{0};
XftDraw* m_xftdraw{nullptr};
XftColor m_xftcolor{};
bool m_xftcolor_allocated{false};
};
POLYBAR_NS_END

View File

@ -8,20 +8,21 @@ list(REMOVE_ITEM SOURCES ipc.cpp)
# Locate dependencies {{{ # Locate dependencies {{{
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
find_package(X11 REQUIRED COMPONENTS Xft Xutil)
find_package(X11_XCB REQUIRED)
find_package(PkgConfig) find_package(PkgConfig)
pkg_check_modules(FONTCONFIG REQUIRED fontconfig) find_package(Threads REQUIRED)
find_package(X11_XCB REQUIRED)
pkg_check_modules(CAIRO REQUIRED cairo-fc)
# pkg_check_modules(PANGOCAIRO REQUIRED pangocairo)
set(APP_LIBRARIES ${APP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) set(APP_LIBRARIES ${APP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
set(APP_LIBRARIES ${APP_LIBRARIES} ${X11_Xft_LIB}) # set(APP_LIBRARIES ${APP_LIBRARIES} ${PANGOCAIRO_LIBRARIES})
#set(APP_LIBRARIES ${APP_LIBRARIES} ${FONTCONFIG_LIBRARIES}) set(APP_LIBRARIES ${APP_LIBRARIES} ${CAIRO_LIBRARIES})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${FONTCONFIG_INCLUDE_DIRS})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/include) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/include)
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/lib/concurrentqueue/include) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/lib/concurrentqueue/include)
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
# set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${PANGOCAIRO_INCLUDE_DIRS})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS})
# xpp library # xpp library
set(XCB_PROTOS xproto) set(XCB_PROTOS xproto)
@ -147,7 +148,6 @@ target_link_libraries(${PROJECT_NAME} Threads::Threads)
target_compile_options(${PROJECT_NAME} PUBLIC target_compile_options(${PROJECT_NAME} PUBLIC
$<$<CXX_COMPILER_ID:GNU>:$<$<CONFIG:MinSizeRel>:-flto>> $<$<CXX_COMPILER_ID:GNU>:$<$<CONFIG:MinSizeRel>:-flto>>
${X11_Xft_DEFINITIONS}
${X11_XCB_DEFINITIONS} ${X11_XCB_DEFINITIONS}
${XCB_DEFINITIONS}) ${XCB_DEFINITIONS})

View File

@ -182,18 +182,32 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
const auto parse_or_throw = [&](string key, string def) { const auto parse_or_throw = [&](string key, string def) {
try { try {
return color::parse(m_conf.get(bs, key, def)); return color_util::parse(m_conf.get(bs, key, def));
} catch (const exception& err) { } catch (const exception& err) {
throw application_error(sstream() << "Failed to set " << key << " (reason: " << err.what() << ")"); throw application_error(sstream() << "Failed to set " << key << " (reason: " << err.what() << ")");
} }
}; };
// Load foreground/background // Load background
for (auto&& step : m_conf.get_list<rgba>(bs, "background", {})) {
m_opts.background_steps.emplace_back(step);
}
if (!m_opts.background_steps.empty()) {
m_opts.background = m_opts.background_steps[0];
if (m_conf.has(bs, "background")) {
m_log.warn("Ignoring `%s.background` (overridden by gradient background)", bs);
}
} else {
m_opts.background = parse_or_throw("background", color_util::hex<uint16_t>(m_opts.background)); m_opts.background = parse_or_throw("background", color_util::hex<uint16_t>(m_opts.background));
}
// Load foreground
m_opts.foreground = parse_or_throw("foreground", color_util::hex<uint16_t>(m_opts.foreground)); m_opts.foreground = parse_or_throw("foreground", color_util::hex<uint16_t>(m_opts.foreground));
// Load over-/underline color and size (warn about deprecated params if used) // Load over-/underline
auto line_color = parse_or_throw("line-color", "#f00"s); auto line_color = m_conf.get(bs, "line-color", "#f00"s);
auto line_size = m_conf.get(bs, "line-size", 0); auto line_size = m_conf.get(bs, "line-size", 0);
m_opts.overline.size = m_conf.get(bs, "overline-size", line_size); m_opts.overline.size = m_conf.get(bs, "overline-size", line_size);
@ -202,8 +216,8 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.underline.color = parse_or_throw("underline-color", line_color); m_opts.underline.color = parse_or_throw("underline-color", line_color);
// Load border settings // Load border settings
auto border_color = m_conf.get(bs, "border-color", ""s);
auto border_size = m_conf.get(bs, "border-size", 0); auto border_size = m_conf.get(bs, "border-size", 0);
auto border_color = m_conf.get(bs, "border-color", "#00000000"s);
m_opts.borders.emplace(edge::TOP, border_settings{}); m_opts.borders.emplace(edge::TOP, border_settings{});
m_opts.borders[edge::TOP].size = m_conf.deprecated(bs, "border-top", "border-top-size", border_size); m_opts.borders[edge::TOP].size = m_conf.deprecated(bs, "border-top", "border-top-size", border_size);
@ -253,8 +267,8 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
throw application_error("Resulting bar height is out of bounds (" + to_string(m_opts.size.h) + ")"); throw application_error("Resulting bar height is out of bounds (" + to_string(m_opts.size.h) + ")");
} }
m_opts.size.w = math_util::cap<int>(m_opts.size.w, 0, m_opts.monitor->w); // 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.size.h = math_util::cap<int>(m_opts.size.h, 0, m_opts.monitor->h);
m_opts.center.y = m_opts.size.h; m_opts.center.y = m_opts.size.h;
m_opts.center.y -= m_opts.borders[edge::BOTTOM].size; m_opts.center.y -= m_opts.borders[edge::BOTTOM].size;
@ -266,41 +280,12 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.center.x /= 2; m_opts.center.x /= 2;
m_opts.center.x += m_opts.borders[edge::LEFT].size; m_opts.center.x += m_opts.borders[edge::LEFT].size;
m_log.trace("bar: Create renderer"); 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);
auto fonts = m_conf.get_list(m_conf.section(), "font", {});
m_renderer = renderer::make(m_opts, move(fonts));
m_log.trace("bar: Attaching sink to registry"); m_log.trace("bar: Attach X event sink");
m_connection.attach_sink(this, SINK_PRIORITY_BAR); m_connection.attach_sink(this, SINK_PRIORITY_BAR);
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); m_log.trace("bar: Attach signal receiver");
m_opts.window = m_renderer->window();
// Subscribe to window enter and leave events
// if we should dim the window
if (m_opts.dimvalue != 1.0) {
m_connection.ensure_event_mask(m_opts.window, XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW);
}
m_log.info("Bar window: %s", m_connection.id(m_opts.window));
restack_window();
m_log.trace("bar: Reconfigure window");
reconfigure_struts();
reconfigure_wm_hints();
m_log.trace("bar: Map window");
m_connection.map_window_checked(m_opts.window);
// Reconfigure window position after mapping (required by Openbox)
// Required by Openbox
reconfigure_pos();
m_log.trace("bar: Drawing empty bar");
m_renderer->begin();
m_renderer->fill_background();
m_renderer->end();
m_sig.attach(this); m_sig.attach(this);
} }
@ -354,8 +339,6 @@ void bar::parse(string&& data, bool force) {
} }
} }
m_renderer->fill_background();
try { try {
m_parser->parse(settings(), data); m_parser->parse(settings(), data);
} catch (const parser_error& err) { } catch (const parser_error& err) {
@ -365,7 +348,7 @@ void bar::parse(string&& data, bool force) {
m_renderer->end(); m_renderer->end();
const auto check_dblclicks = [&]() -> bool { const auto check_dblclicks = [&]() -> bool {
for (auto&& action : m_renderer->get_actions()) { for (auto&& action : m_renderer->actions()) {
if (static_cast<uint8_t>(action.button) >= static_cast<uint8_t>(mousebtn::DOUBLE_LEFT)) { if (static_cast<uint8_t>(action.button) >= static_cast<uint8_t>(mousebtn::DOUBLE_LEFT)) {
return true; return true;
} }
@ -510,7 +493,7 @@ void bar::handle(const evt::destroy_notify& evt) {
* _NET_WM_WINDOW_OPACITY atom value * _NET_WM_WINDOW_OPACITY atom value
*/ */
void bar::handle(const evt::enter_notify&) { void bar::handle(const evt::enter_notify&) {
#if DEBUG #ifdef DEBUG_SHADED
if (m_opts.origin == edge::TOP) { if (m_opts.origin == edge::TOP) {
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::unshade_window{}); }); m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::unshade_window{}); });
return; return;
@ -534,7 +517,7 @@ void bar::handle(const evt::enter_notify&) {
* _NET_WM_WINDOW_OPACITY atom value * _NET_WM_WINDOW_OPACITY atom value
*/ */
void bar::handle(const evt::leave_notify&) { void bar::handle(const evt::leave_notify&) {
#if DEBUG #ifdef DEBUG_SHADED
if (m_opts.origin == edge::TOP) { if (m_opts.origin == edge::TOP) {
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::shade_window{}); }); m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::shade_window{}); });
return; return;
@ -571,7 +554,7 @@ void bar::handle(const evt::button_press& evt) {
m_buttonpress_pos = evt->event_x; m_buttonpress_pos = evt->event_x;
const auto deferred_fn = [&](size_t) { const auto deferred_fn = [&](size_t) {
for (auto&& action : m_renderer->get_actions()) { for (auto&& action : m_renderer->actions()) {
if (action.button == m_buttonpress_btn && !action.active && action.test(m_buttonpress_pos)) { if (action.button == m_buttonpress_btn && !action.active && action.test(m_buttonpress_pos)) {
m_log.trace("Found matching input area"); m_log.trace("Found matching input area");
m_sig.emit(button_press{string{action.command}}); m_sig.emit(button_press{string{action.command}});
@ -626,7 +609,7 @@ void bar::handle(const evt::expose& evt) {
} }
m_log.trace("bar: Received expose event"); m_log.trace("bar: Received expose event");
m_renderer->flush(false); m_renderer->flush();
} }
} }
@ -643,7 +626,7 @@ void bar::handle(const evt::expose& evt) {
* pseudo-transparent background when it changes * pseudo-transparent background when it changes
*/ */
void bar::handle(const evt::property_notify& evt) { void bar::handle(const evt::property_notify& evt) {
#ifdef DEBUG_LOGGER_TRACE #ifdef DEBUG_LOGGER_VERBOSE
string atom_name = m_connection.get_atom_name(evt->atom).name(); string atom_name = m_connection.get_atom_name(evt->atom).name();
m_log.trace_x("bar: property_notify(%s)", atom_name); m_log.trace_x("bar: property_notify(%s)", atom_name);
#endif #endif
@ -654,9 +637,42 @@ void bar::handle(const evt::property_notify& evt) {
} }
bool bar::on(const signals::eventqueue::start&) { bool bar::on(const signals::eventqueue::start&) {
m_log.trace("bar: Create renderer");
m_renderer = renderer::make(m_opts);
m_opts.window = m_renderer->window();
// Subscribe to window enter and leave events
// if we should dim the window
if (m_opts.dimvalue != 1.0) {
m_connection.ensure_event_mask(m_opts.window, XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW);
}
m_log.info("Bar window: %s", m_connection.id(m_opts.window));
restack_window();
m_log.trace("bar: Reconfigure window");
reconfigure_struts();
reconfigure_wm_hints();
m_log.trace("bar: Map window");
m_connection.map_window_checked(m_opts.window);
// Reconfigure window position after mapping (required by Openbox)
// Required by Openbox
reconfigure_pos();
m_log.trace("bar: Draw empty bar");
m_renderer->begin();
m_renderer->end();
m_sig.emit(signals::ui::ready{});
// TODO: tray manager could run this internally on ready event
m_log.trace("bar: Setup tray manager"); m_log.trace("bar: Setup tray manager");
m_tray->setup(static_cast<const bar_settings&>(m_opts)); m_tray->setup(static_cast<const bar_settings&>(m_opts));
broadcast_visibility(); broadcast_visibility();
return true; return true;
} }
@ -677,7 +693,7 @@ bool bar::on(const signals::ui::unshade_window&) {
m_sig.emit(signals::ui::tick{}); m_sig.emit(signals::ui::tick{});
} }
if (!remaining) { if (!remaining) {
m_renderer->flush(false); m_renderer->flush();
} }
if (m_opts.dimmed) { if (m_opts.dimmed) {
m_opts.dimmed = false; m_opts.dimmed = false;
@ -716,7 +732,7 @@ bool bar::on(const signals::ui::shade_window&) {
m_sig.emit(signals::ui::tick{}); m_sig.emit(signals::ui::tick{});
} }
if (!remaining) { if (!remaining) {
m_renderer->flush(false); m_renderer->flush();
} }
if (!m_opts.dimmed) { if (!m_opts.dimmed) {
m_opts.dimmed = true; m_opts.dimmed = true;

View File

@ -2,13 +2,12 @@
#include <fstream> #include <fstream>
#include "components/config.hpp" #include "components/config.hpp"
#include "utils/color.hpp"
#include "utils/env.hpp" #include "utils/env.hpp"
#include "utils/factory.hpp" #include "utils/factory.hpp"
#include "utils/file.hpp" #include "utils/file.hpp"
#include "utils/math.hpp" #include "utils/math.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
#include "x11/color.hpp"
#include "x11/xresources.hpp"
POLYBAR_NS POLYBAR_NS
@ -276,8 +275,15 @@ chrono::duration<double> config::convert(string&& value) const {
} }
template <> template <>
color config::convert(string&& value) const { rgba config::convert(string&& value) const {
return color{forward<string>(value)}; auto color = color_util::parse(value, 0);
// clang-format off
return rgba{
color_util::red_channel<uint8_t>(color) / 255.0,
color_util::green_channel<uint8_t>(color) / 255.0,
color_util::blue_channel<uint8_t>(color) / 255.0,
color_util::alpha_channel<uint8_t>(color) / 255.0};
// clang-format on
} }
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -148,11 +148,8 @@ controller::~controller() {
*/ */
bool controller::run(bool writeback) { bool controller::run(bool writeback) {
m_log.info("Starting application"); m_log.info("Starting application");
assert(!m_connection.connection_has_error()); assert(!m_connection.connection_has_error());
m_writeback = writeback; m_writeback = writeback;
m_sig.attach(this); m_sig.attach(this);
size_t started_modules{0}; size_t started_modules{0};
@ -183,10 +180,7 @@ bool controller::run(bool writeback) {
throw application_error("No modules started"); throw application_error("No modules started");
} }
m_sig.emit(signals::eventqueue::start{});
m_connection.flush(); m_connection.flush();
m_event_thread = thread(&controller::process_eventqueue, this); m_event_thread = thread(&controller::process_eventqueue, this);
read_events(); read_events();
@ -205,6 +199,9 @@ bool controller::run(bool writeback) {
* Enqueue event * Enqueue event
*/ */
bool controller::enqueue(event&& evt) { bool controller::enqueue(event&& evt) {
if (!m_process_events && evt.type != event_type::QUIT) {
return false;
}
if (!m_queue.enqueue(forward<decltype(evt)>(evt))) { if (!m_queue.enqueue(forward<decltype(evt)>(evt))) {
m_log.warn("Failed to enqueue event"); m_log.warn("Failed to enqueue event");
return false; return false;
@ -330,8 +327,7 @@ void controller::read_events() {
*/ */
void controller::process_eventqueue() { void controller::process_eventqueue() {
m_log.info("Eventqueue worker (thread-id=%lu)", this_thread::get_id()); m_log.info("Eventqueue worker (thread-id=%lu)", this_thread::get_id());
m_sig.emit(signals::eventqueue::start{});
enqueue(make_update_evt(true));
while (!g_terminate) { while (!g_terminate) {
event evt{}; event evt{};
@ -556,6 +552,16 @@ bool controller::on(const signals::eventqueue::check_state&) {
return true; return true;
} }
/**
* Process ui ready event
*/
bool controller::on(const signals::ui::ready&) {
m_process_events = true;
enqueue(make_update_evt(true));
// let the event bubble
return false;
}
/** /**
* Process ui button press event * Process ui button press event
*/ */

View File

@ -2,6 +2,7 @@
#include "components/logger.hpp" #include "components/logger.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "settings.hpp"
#include "utils/concurrency.hpp" #include "utils/concurrency.hpp"
#include "utils/factory.hpp" #include "utils/factory.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
@ -60,9 +61,9 @@ logger::logger(loglevel level) : m_level(level) {
* Set output verbosity * Set output verbosity
*/ */
void logger::verbosity(loglevel&& level) { void logger::verbosity(loglevel&& level) {
#ifndef DEBUG #ifndef DEBUG_LOGGER
if (level == loglevel::TRACE) { if (level == loglevel::TRACE) {
throw application_error("Trace logging is only enabled for debug builds..."); throw application_error("Trace logging is not enabled...");
} }
#endif #endif
m_level = forward<decltype(level)>(level); m_level = forward<decltype(level)>(level);

View File

@ -173,60 +173,8 @@ size_t parser::text(string&& data) {
} }
#endif #endif
const uint8_t* utf{reinterpret_cast<const uint8_t*>(&data[0])}; m_sig.emit(signals::parser::text{forward<string>(data)});
return data.size();
// clang-format off
if (utf[0] < 0x80) {
size_t pos{0};
// grab consecutive ascii chars
while (utf[pos] && utf[pos] < 0x80) {
packet pkt{};
size_t limit{memory_util::countof(pkt.data)};
size_t len{0};
while (len + 1 < limit && utf[pos] && utf[pos] < 0x80) {
pkt.data[len++] = utf[pos++];
}
if (!len) {
break;
}
pkt.length = len;
m_sig.emit(write_text_string{move(pkt)});
}
if (pos > 0) {
return pos;
}
} else if ((utf[0] & 0xe0) == 0xc0) { // 2 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>(((utf[0] & 0x1f) << 6) | (utf[1] & 0x3f))});
return 2;
} else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>(((utf[0] & 0x0f) << 12) | ((utf[1] & 0x3f) << 6) | (utf[2] & 0x3f))});
return 3;
} else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence
// m_sig.emit(write_text_unicode{((utf[0] & 0x07) << 18) | ((utf[1] & 0x3f) << 12) | ((utf[2] & 0x3f) << 6) | (utf[3] & 0x3f)});
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 4;
} else if ((utf[0] & 0xfc) == 0xf8) { // 5 byte utf-8 sequence
// m_sig.emit(write_text_unicode{((utf[0] & 0x03) << 24) | ((utf[1] & 0x3f) << 18) | ((utf[2] & 0x3f) << 12) | ((utf[3] & 0x3f) << 6) | (utf[4] & 0x3f)});
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 5;
} else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence
// m_sig.emit(write_text_unicode{((utf[0] & 0x01) << 30) | ((utf[1] & 0x3f) << 24) | ((utf[2] & 0x3f) << 18) | ((utf[3] & 0x3f) << 12) | ((utf[4] & 0x3f) << 6) | (utf[5] & 0x3f)});
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 6;
}
// clang-format on
if (utf[0] < 0x80) {
m_sig.emit(write_text_ascii{utf[0]});
}
return 1;
} }
/** /**

View File

@ -1,11 +1,19 @@
#include "components/renderer.hpp" #include "components/renderer.hpp"
#include "cairo/context.hpp"
#include "cairo/font.hpp"
#include "cairo/surface.hpp"
#include "cairo/types.hpp"
#include "cairo/utils.hpp"
#include "components/config.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "events/signal.hpp" #include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_receiver.hpp" #include "events/signal_receiver.hpp"
#include "utils/color.hpp"
#include "utils/factory.hpp" #include "utils/factory.hpp"
#include "utils/file.hpp" #include "utils/file.hpp"
#include "utils/math.hpp"
#include "utils/string.hpp"
#include "x11/atoms.hpp" #include "x11/atoms.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/draw.hpp" #include "x11/draw.hpp"
@ -18,53 +26,50 @@ POLYBAR_NS
/** /**
* Create instance * Create instance
*/ */
renderer::make_type renderer::make(const bar_settings& bar, vector<string>&& fonts) { renderer::make_type renderer::make(const bar_settings& bar) {
// clang-format off // clang-format off
return factory_util::unique<renderer>( return factory_util::unique<renderer>(
connection::make(), connection::make(),
signal_emitter::make(), signal_emitter::make(),
config::make(),
logger::make(), logger::make(),
font_manager::make(), forward<decltype(bar)>(bar));
forward<decltype(bar)>(bar),
forward<decltype(fonts)>(fonts));
// clang-format on // clang-format on
} }
/** /**
* Construct renderer instance * Construct renderer instance
*/ */
renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logger, renderer::renderer(
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts) connection& conn, signal_emitter& sig, const config& conf, const logger& logger, const bar_settings& bar)
: m_connection(conn) : m_connection(conn)
, m_sig(emitter) , m_sig(sig)
, m_conf(conf)
, m_log(logger) , m_log(logger)
, m_fontmanager(forward<decltype(font_manager)>(font_manager))
, m_bar(forward<const bar_settings&>(bar)) , m_bar(forward<const bar_settings&>(bar))
, m_rect(m_bar.inner_area()) { , m_rect(m_bar.inner_area()) {
m_sig.attach(this); m_sig.attach(this);
m_log.trace("renderer: Get TrueColor visual"); m_log.trace("renderer: Get TrueColor visual");
{
if ((m_visual = m_connection.visual_type(m_connection.screen(), 32)) == nullptr) { if ((m_visual = m_connection.visual_type(m_connection.screen(), 32)) == nullptr) {
m_log.err("No 32-bit TrueColor visual found..."); m_log.err("No 32-bit TrueColor visual found...");
if ((m_visual = m_connection.visual_type(m_connection.screen(), 24)) == nullptr) { if ((m_visual = m_connection.visual_type(m_connection.screen(), 24)) == nullptr) {
m_log.err("No 24-bit TrueColor visual found, aborting..."); m_log.err("No 24-bit TrueColor visual found...");
throw application_error("No matching TrueColor visual found..."); } else {
}
if (m_visual == nullptr) {
throw application_error("No matching TrueColor visual found...");
}
m_depth = 24; m_depth = 24;
}
m_fontmanager->set_visual(m_connection.visual(m_depth)); }
if (m_visual == nullptr) {
throw application_error("No matching TrueColor");
}
} }
m_log.trace("renderer: Allocate colormap"); m_log.trace("renderer: Allocate colormap");
{
m_colormap = m_connection.generate_id(); m_colormap = m_connection.generate_id();
m_connection.create_colormap(XCB_COLORMAP_ALLOC_NONE, m_colormap, m_connection.screen()->root, m_visual->visual_id); m_connection.create_colormap(XCB_COLORMAP_ALLOC_NONE, m_colormap, m_connection.screen()->root, m_visual->visual_id);
}
m_log.trace("renderer: Allocate output window"); m_log.trace("renderer: Allocate output window");
{ {
@ -88,73 +93,61 @@ renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logg
} }
m_log.trace("renderer: Allocate window pixmap"); m_log.trace("renderer: Allocate window pixmap");
m_pixmap = m_connection.generate_id();
m_connection.create_pixmap(m_depth, m_pixmap, m_window, m_rect.width, m_rect.height);
m_log.trace("renderer: Allocate graphic contexts");
{ {
// clang-format off m_pixmap = m_connection.generate_id();
vector<uint32_t> colors { m_connection.create_pixmap(m_depth, m_pixmap, m_window, m_bar.size.w, m_bar.size.h);
m_bar.background, }
m_bar.foreground,
m_bar.overline.color,
m_bar.underline.color,
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 = 0; i < 8; i++) { m_log.trace("renderer: Allocate graphic context");
{
uint32_t mask{0}; uint32_t mask{0};
uint32_t value_list[32]{0}; uint32_t value_list[32]{0};
xcb_params_gc_t params{}; xcb_params_gc_t params{};
XCB_AUX_ADD_PARAM(&mask, &params, foreground, colors[i]); XCB_AUX_ADD_PARAM(&mask, &params, foreground, m_bar.foreground);
XCB_AUX_ADD_PARAM(&mask, &params, graphics_exposures, 0); XCB_AUX_ADD_PARAM(&mask, &params, graphics_exposures, 0);
connection::pack_values(mask, &params, value_list); connection::pack_values(mask, &params, value_list);
m_gcontext = m_connection.generate_id();
m_colors.emplace(gc(i), colors[i]); m_connection.create_gc(m_gcontext, m_pixmap, mask, value_list);
m_gcontexts.emplace(gc(i), m_connection.generate_id());
m_connection.create_gc(m_gcontexts.at(gc(i)), m_pixmap, mask, value_list);
} }
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_log.trace("renderer: Load fonts"); m_log.trace("renderer: Load fonts");
{ {
auto fonts_loaded = false; auto fonts = m_conf.get_list<string>(m_conf.section(), "font", {});
auto fontindex = 0;
if (fonts.empty()) { if (fonts.empty()) {
m_log.warn("No fonts specified, using fallback font \"fixed\""); m_log.warn("No fonts specified, using fallback font \"fixed\"");
fonts.emplace_back("fixed");
} }
auto fonts_loaded = false;
for (const auto& f : fonts) { for (const auto& f : fonts) {
fontindex++;
vector<string> fd{string_util::split(f, ';')}; vector<string> fd{string_util::split(f, ';')};
string pattern{fd[0]}; auto font = cairo::make_font(*m_context, string(fd[0]), fd.size() > 1 ? std::atoi(fd[1].c_str()) : 0);
int offset{0}; m_log.info("Loaded font \"%s\" (name=%s, file=%s)", fd[0], font->name(), font->file());
*m_context << move(font);
if (fd.size() > 1) {
offset = std::stoi(fd[1], nullptr, 10);
}
if (m_fontmanager->load(pattern, fontindex, offset)) {
fonts_loaded = true; fonts_loaded = true;
} else {
m_log.warn("Unable to load font '%s'", fd[0]);
}
} }
if (!fonts_loaded && !fonts.empty()) { if (!fonts_loaded) {
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"); throw application_error("Unable to load fonts");
} }
m_fontmanager->allocate_color(m_bar.foreground);
} }
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);
} }
/** /**
@ -162,10 +155,6 @@ renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logg
*/ */
renderer::~renderer() { renderer::~renderer() {
m_sig.detach(this); m_sig.detach(this);
if (m_window != XCB_NONE) {
m_connection.destroy_window(m_window);
}
} }
/** /**
@ -175,17 +164,51 @@ xcb_window_t renderer::window() const {
return m_window; return m_window;
} }
/**
* Get completed action blocks
*/
const vector<action_block> renderer::actions() const {
return m_actions;
}
/** /**
* Begin render routine * Begin render routine
*/ */
void renderer::begin() { void renderer::begin() {
m_log.trace_x("renderer: begin"); m_log.trace_x("renderer: begin");
m_rect = m_bar.inner_area(); // Reset state
m_alignment = alignment::NONE;
m_currentx = 0;
m_attributes = 0;
m_actions.clear(); m_actions.clear();
m_attributes.reset();
m_alignment = 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;
// 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_context->save();
fill_background();
fill_borders();
// clang-format off
m_context->clip(cairo::rect{
static_cast<double>(m_rect.x),
static_cast<double>(m_rect.y),
static_cast<double>(m_rect.width),
static_cast<double>(m_rect.height)});
// clang-format on
} }
/** /**
@ -194,101 +217,39 @@ void renderer::begin() {
void renderer::end() { void renderer::end() {
m_log.trace_x("renderer: end"); m_log.trace_x("renderer: end");
m_fontmanager->cleanup(); highlight_clickable_areas();
#ifdef DEBUG_HINTS m_context->restore();
debug_hints(); m_surface->flush();
#endif
flush(false); flush();
} }
/** /**
* Flush pixmap contents onto the target window * Flush pixmap contents onto the target window
*/ */
void renderer::flush(bool clear) { void renderer::flush() {
const xcb_rectangle_t& r = m_rect; m_log.trace_x("renderer: flush");
xcb_rectangle_t top{0, 0, 0U, 0U}; #ifdef DEBUG_SHADED
top.x += m_bar.borders.at(edge::LEFT).size;
top.width += m_bar.size.w - m_bar.borders.at(edge::LEFT).size - m_bar.borders.at(edge::RIGHT).size;
top.height += m_bar.borders.at(edge::TOP).size;
xcb_rectangle_t bottom{0, 0, 0U, 0U};
bottom.x += m_bar.borders.at(edge::LEFT).size;
bottom.y += m_bar.size.h - m_bar.borders.at(edge::BOTTOM).size;
bottom.width += m_bar.size.w - m_bar.borders.at(edge::LEFT).size - m_bar.borders.at(edge::RIGHT).size;
bottom.height += m_bar.borders.at(edge::BOTTOM).size;
xcb_rectangle_t left{0, 0, 0U, 0U};
left.width += m_bar.borders.at(edge::LEFT).size;
left.height += m_bar.size.h;
xcb_rectangle_t right{0, 0, 0U, 0U};
right.x += m_bar.size.w - m_bar.borders.at(edge::RIGHT).size;
right.width += m_bar.borders.at(edge::RIGHT).size;
right.height += m_bar.size.h;
// Calculate the area that was reserved so that we
// can clear any previous content drawn at the same location
xcb_rectangle_t clear_area{r.x, r.y, r.width, r.height};
if (m_cleararea.size && m_cleararea.side == edge::RIGHT) {
clear_area.x += r.width;
clear_area.y = top.height;
clear_area.width = m_cleararea.size;
} else if (m_cleararea.size && m_cleararea.side == edge::LEFT) {
clear_area.x = left.width;
clear_area.y = top.height;
clear_area.width = m_cleararea.size;
} else if (m_cleararea.size && m_cleararea.side == edge::TOP) {
clear_area.height = m_cleararea.size;
} else if (m_cleararea.size && m_cleararea.side == edge::BOTTOM) {
clear_area.y += r.height - m_cleararea.size;
clear_area.height = m_cleararea.size;
}
if (clear_area != m_cleared && clear_area != 0) {
m_log.trace("renderer: clearing area %dx%d+%d+%d", clear_area.width, clear_area.height, clear_area.x, clear_area.y);
m_connection.clear_area(0, m_window, clear_area.x, clear_area.y, clear_area.width, clear_area.height);
m_cleared = clear_area;
}
#if DEBUG
if (m_bar.shaded && m_bar.origin == edge::TOP) { if (m_bar.shaded && m_bar.origin == edge::TOP) {
m_log.trace("renderer: copy pixmap (shaded=1, clear=%i, geom=%dx%d+%d+%d)", clear, r.width, r.height, r.x, r.y); m_log.trace_x(
"renderer: copy pixmap (shaded=1, geom=%dx%d+%d+%d)", m_rect.width, m_rect.height, m_rect.x, m_rect.y);
auto geom = m_connection.get_geometry(m_window); auto geom = m_connection.get_geometry(m_window);
auto x1 = 0; auto x1 = 0;
auto y1 = r.height - m_bar.shade_size.h - r.y - geom->height; auto y1 = m_rect.height - m_bar.shade_size.h - m_rect.y - geom->height;
auto x2 = r.x; auto x2 = m_rect.x;
auto y2 = r.y; auto y2 = m_rect.y;
auto w = r.width; auto w = m_rect.width;
auto h = r.height - m_bar.shade_size.h + geom->height; auto h = m_rect.height - m_bar.shade_size.h + geom->height;
m_connection.copy_area(m_pixmap, m_window, m_gcontexts.at(gc::FG), x1, y1, x2, y2, w, h); m_connection.copy_area(m_pixmap, m_window, m_gcontext, x1, y1, x2, y2, w, h);
m_connection.flush(); m_connection.flush();
return; return;
} }
#endif #endif
m_log.trace("renderer: copy pixmap (clear=%i, geom=%dx%d+%d+%d)", clear, r.width, r.height, r.x, r.y); 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_gcontexts.at(gc::FG), 0, 0, r.x, r.y, r.width, r.height); m_connection.copy_area(m_pixmap, m_window, m_gcontext, 0, 0, 0, 0, m_bar.size.w, m_bar.size.h);
m_log.trace_x("renderer: draw top border (%lupx, %08x)", top.height, m_bar.borders.at(edge::TOP).color);
draw_util::fill(m_connection, m_window, m_gcontexts.at(gc::BT), top);
m_log.trace_x("renderer: draw bottom border (%lupx, %08x)", bottom.height, m_bar.borders.at(edge::BOTTOM).color);
draw_util::fill(m_connection, m_window, m_gcontexts.at(gc::BB), bottom);
m_log.trace_x("renderer: draw left border (%lupx, %08x)", left.width, m_bar.borders.at(edge::LEFT).color);
draw_util::fill(m_connection, m_window, m_gcontexts.at(gc::BL), left);
m_log.trace_x("renderer: draw right border (%lupx, %08x)", right.width, m_bar.borders.at(edge::RIGHT).color);
draw_util::fill(m_connection, m_window, m_gcontexts.at(gc::BR), right);
if (clear) {
m_connection.clear_area(false, m_pixmap, 0, 0, r.width, r.height);
}
m_connection.flush(); m_connection.flush();
} }
@ -327,391 +288,296 @@ void renderer::reserve_space(edge side, uint16_t w) {
} }
} }
/**
* Check if given attribute is enabled
*/
bool renderer::check_attribute(const attribute attr) {
return (m_attributes >> static_cast<uint8_t>(attr)) & 1U;
}
/** /**
* Fill background color * Fill background color
*/ */
void renderer::fill_background() { void renderer::fill_background() {
m_log.trace_x("renderer: fill_background"); m_context->save();
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), 0, 0, m_rect.width, m_rect.height); *m_context << m_compositing_background;
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};
} else {
m_log.trace_x("renderer: solid background #%08x", m_bar.background);
*m_context << m_bar.background;
}
m_context->paint();
m_context->restore();
} }
/** /**
* Fill overline color * Fill overline color
*/ */
void renderer::fill_overline(int16_t x, uint16_t w) { void renderer::fill_overline(double x, double w) {
if (!check_attribute(attribute::OVERLINE)) { if (m_bar.overline.size && m_attributes.test(static_cast<uint8_t>(attribute::OVERLINE))) {
return m_log.trace_x("renderer: not filling overline (flag unset)"); m_log.trace_x("renderer: overline(x=%i, w=%i)", x, w);
} else if (!m_bar.overline.size) { m_context->save();
return m_log.trace_x("renderer: not filling overline (size=0)"); *m_context << m_compositing_overline;
*m_context << m_color_overline;
*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();
} }
m_log.trace_x("renderer: fill_overline(%i, #%08x)", m_bar.overline.size, m_colors[gc::OL]);
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, 0, w, m_bar.overline.size);
} }
/** /**
* Fill underline color * Fill underline color
*/ */
void renderer::fill_underline(int16_t x, uint16_t w) { void renderer::fill_underline(double x, double w) {
if (!check_attribute(attribute::UNDERLINE)) { if (m_bar.underline.size && m_attributes.test(static_cast<uint8_t>(attribute::UNDERLINE))) {
return m_log.trace_x("renderer: not filling underline (flag unset)"); m_log.trace_x("renderer: underline(x=%i, w=%i)", x, w);
} else if (!m_bar.underline.size) { m_context->save();
return m_log.trace_x("renderer: not filling underline (size=0)"); *m_context << m_compositing_underline;
} *m_context << m_color_underline;
m_log.trace_x("renderer: fill_underline(%i, #%08x)", m_bar.underline.size, m_colors[gc::UL]); *m_context << cairo::rect{x, static_cast<double>(m_rect.y + m_rect.height - m_bar.underline.size), w,
int16_t y{static_cast<int16_t>(m_rect.height - m_bar.underline.size)}; static_cast<double>(m_bar.underline.size)};
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x, y, w, m_bar.underline.size); m_context->fill();
} m_context->restore();
/**
* @see shift_content
*/
void renderer::fill_shift(const int16_t px) {
shift_content(px);
}
/**
* Draw consecutive character glyphs
*/
void renderer::draw_textstring(const uint16_t* text, size_t len) {
m_log.trace_x("renderer: draw_textstring(\"%s\")", text);
for (size_t n = 0; n < len; n++) {
vector<uint16_t> chars{text[n]};
shared_ptr<font_ref> font{m_fontmanager->match_char(chars[0])};
uint8_t width{static_cast<const uint8_t>(m_fontmanager->glyph_width(font, chars[0]) * chars.size())};
if (!font) {
m_log.warn("Could not find glyph for %i", chars[0]);
continue;
} else if (!width) {
m_log.warn("Could not determine glyph width for %i", chars[0]);
continue;
}
while (n + 1 < len && text[n + 1] == chars[0]) {
chars.emplace_back(text[n++]);
}
width *= chars.size();
auto x = shift_content(width);
auto y = m_rect.height / 2 + font->height / 2 - font->descent + font->offset_y;
if (font->ptr != XCB_NONE && m_gcfont != font->ptr) {
const uint32_t v[1]{font->ptr};
m_connection.change_gc(m_gcontexts.at(gc::FG), XCB_GC_FONT, v);
m_gcfont = font->ptr;
}
m_fontmanager->drawtext(font, m_pixmap, m_gcontexts.at(gc::FG), x, y, chars.data(), chars.size());
fill_underline(x, width);
fill_overline(x, width);
} }
} }
/** /**
* Get completed action blocks * Fill border colors
*/ */
const vector<action_block> renderer::get_actions() { void renderer::fill_borders() {
return m_actions; m_context->save();
*m_context << m_compositing_borders;
cairo::rect top{0.0, 0.0, 0.0, 0.0};
top.x += m_bar.borders.at(edge::LEFT).size;
top.w += m_bar.size.w - m_bar.borders.at(edge::LEFT).size - m_bar.borders.at(edge::TOP).size;
top.h += m_bar.borders.at(edge::TOP).size;
m_log.trace_x("renderer: border T(%.0f, #%08x)", top.h, m_bar.borders.at(edge::TOP).color);
(*m_context << top << m_bar.borders.at(edge::TOP).color).fill();
cairo::rect bottom{0.0, 0.0, 0.0, 0.0};
bottom.x += m_bar.borders.at(edge::LEFT).size;
bottom.y += m_bar.size.h - m_bar.borders.at(edge::BOTTOM).size;
bottom.w += m_bar.size.w - m_bar.borders.at(edge::LEFT).size - m_bar.borders.at(edge::RIGHT).size;
bottom.h += m_bar.borders.at(edge::BOTTOM).size;
m_log.trace_x("renderer: border B(%.0f, #%08x)", bottom.h, m_bar.borders.at(edge::BOTTOM).color);
(*m_context << bottom << m_bar.borders.at(edge::BOTTOM).color).fill();
cairo::rect left{0.0, 0.0, 0.0, 0.0};
left.w += m_bar.borders.at(edge::LEFT).size;
left.h += m_bar.size.h;
m_log.trace_x("renderer: border L(%.0f, #%08x)", left.w, m_bar.borders.at(edge::LEFT).color);
(*m_context << left << m_bar.borders.at(edge::LEFT).color).fill();
cairo::rect right{0.0, 0.0, 0.0, 0.0};
right.x += m_bar.size.w - m_bar.borders.at(edge::RIGHT).size;
right.w += m_bar.borders.at(edge::RIGHT).size;
right.h += m_bar.size.h;
m_log.trace_x("renderer: border R(%.0f, #%08x)", right.w, m_bar.borders.at(edge::RIGHT).color);
(*m_context << right << m_bar.borders.at(edge::RIGHT).color).fill();
m_context->restore();
} }
/** /**
* Shift pixmap content by given value * Draw text contents
*/ */
int16_t renderer::shift_content(int16_t x, const int16_t shift_x) { void renderer::draw_text(const string& contents) {
if (x > m_rect.width) { m_log.trace_x("renderer: text(%s)", contents.c_str());
return m_rect.width;
} else if (x < 0) { cairo_text_extents_t extents;
return 0; cairo_text_extents(*m_context, contents.c_str(), &extents);
if (!extents.width) {
return;
} }
m_log.trace_x("renderer: shift_content(%i)", shift_x); cairo::abspos origin{static_cast<double>(m_rect.x), static_cast<double>(m_rect.y)};
int16_t base_x{0}; if (m_alignment == alignment::CENTER) {
double delta{0.0}; origin.x += m_rect.width / 2.0 - extents.width / 2.0;
adjust_clickable_areas(extents.width / 2.0);
switch (m_alignment) { } else if (m_alignment == alignment::RIGHT) {
case alignment::NONE: origin.x += m_rect.width - extents.width;
break; adjust_clickable_areas(extents.width);
case alignment::LEFT: } else {
break; origin.x += m_x;
case alignment::CENTER:
base_x = static_cast<int16_t>(m_rect.width / 2);
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_rect.height);
x = base_x - (x + shift_x) / 2 + x;
delta = static_cast<double>(shift_x) / 2;
break;
case alignment::RIGHT:
base_x = static_cast<int16_t>(m_rect.width - x);
m_connection.copy_area(
m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), base_x, 0, base_x - shift_x, 0, x, m_rect.height);
x = m_rect.width - shift_x;
delta = static_cast<double>(shift_x);
break;
} }
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), x, 0, m_rect.width - x, 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();
// }
// Translate pos of clickable areas origin.y += m_rect.height / 2.0;
if (m_alignment != alignment::LEFT) {
m_context->save();
*m_context << origin;
*m_context << m_compositing_foreground;
*m_context << m_color_foreground;
*m_context << cairo::textblock{contents, m_fontindex};
m_context->position(&m_x, &m_y);
m_context->restore();
// if (m_alignment == alignment::CENTER) {
// m_x += extents.width / 2.0;
// } else {
// m_x += extents.width;
// }
// 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) { for (auto&& action : m_actions) {
if (action.active || action.align != m_alignment) { if (!action.active && action.align == m_alignment) {
continue;
}
action.start_x -= delta; action.start_x -= delta;
action.end_x -= delta; action.end_x -= delta;
} }
} }
m_currentx += shift_x;
return x;
} }
/**
* @see shift_content
*/
int16_t renderer::shift_content(const int16_t shift_x) {
return shift_content(m_currentx, shift_x);
}
#ifdef DEBUG_HINTS
/** /**
* Draw boxes at the location of each created action block * Draw boxes at the location of each created action block
*/ */
void renderer::debug_hints() { void renderer::highlight_clickable_areas() {
uint16_t border_width{1}; #ifdef DEBUG_HINTS
map<alignment, int> hint_num{{ map<alignment, int> hint_num{};
// clang-format off
{alignment::LEFT, 0},
{alignment::CENTER, 0},
{alignment::RIGHT, 0},
// clang-format on
}};
m_debughints.clear();
for (auto&& action : m_actions) { for (auto&& action : m_actions) {
if (action.active) { if (!action.active) {
continue; uint8_t 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 w = action.width();
double h = m_rect.height;
xcb_window_t hintwin{m_connection.generate_id()}; m_context->save();
m_debughints.emplace_back(hintwin); *m_context << CAIRO_OPERATOR_OVERLAY << (n % 2 ? 0x55FF0000 : 0x5500FF00);
*m_context << cairo::rect{x, y, w, h};
uint8_t num{static_cast<uint8_t>(hint_num.find(action.align)->second++)}; m_context->fill();
m_context->restore();
// clang-format off
winspec(m_connection, hintwin)
<< cw_size(action.width() - border_width * 2, m_rect.height - border_width * 2)
<< cw_pos(action.start_x + num * DEBUG_HINTS_OFFSET_X, m_bar.pos.y + m_rect.y + num * DEBUG_HINTS_OFFSET_Y)
<< cw_border(border_width)
<< cw_depth(m_depth)
<< cw_visual(m_visual->visual_id)
<< cw_params_colormap(m_colormap)
<< cw_params_back_pixel(0)
<< cw_params_border_pixel(num % 2 ? 0xFFFF0000 : 0xFF00FF00)
<< cw_params_override_redirect(true)
<< cw_flush()
;
// clang-format on
const uint32_t shadow{0};
m_connection.change_property(XCB_PROP_MODE_REPLACE, hintwin, _COMPTON_SHADOW, XCB_ATOM_CARDINAL, 32, 1, &shadow);
m_connection.map_window(hintwin);
} }
} }
#endif #endif
}
bool renderer::on(const change_background& evt) { bool renderer::on(const signals::parser::change_background& evt) {
const uint32_t color{evt.cast()}; const uint32_t color{evt.cast()};
if (color != m_color_background) {
if (m_colors[gc::BG] == color) { m_color_background = color;
m_log.trace_x("renderer: ignoring unchanged background color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_background(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::BG), XCB_GC_FOREGROUND, &color);
m_colors[gc::BG] = color;
shift_content(0);
} }
return true; return true;
} }
bool renderer::on(const change_foreground& evt) { bool renderer::on(const signals::parser::change_foreground& evt) {
const uint32_t color{evt.cast()}; const uint32_t color{evt.cast()};
if (color != m_color_foreground) {
if (m_colors[gc::FG] == color) { m_color_foreground = color;
m_log.trace_x("renderer: ignoring unchanged foreground color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_foreground(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::FG), XCB_GC_FOREGROUND, &color);
m_fontmanager->allocate_color(color);
m_colors[gc::FG] = color;
} }
return true; return true;
} }
bool renderer::on(const change_underline& evt) { bool renderer::on(const signals::parser::change_underline& evt) {
const uint32_t color{evt.cast()}; const uint32_t color{evt.cast()};
if (color != m_color_underline) {
if (m_colors[gc::UL] == color) { m_color_underline = color;
m_log.trace_x("renderer: ignoring unchanged underline color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_underline(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::UL), XCB_GC_FOREGROUND, &color);
m_colors[gc::UL] = color;
} }
return true; return true;
} }
bool renderer::on(const change_overline& evt) { bool renderer::on(const signals::parser::change_overline& evt) {
const uint32_t color{evt.cast()}; const uint32_t color{evt.cast()};
if (color != m_color_overline) {
if (m_colors[gc::OL] == color) { m_color_overline = color;
m_log.trace_x("renderer: ignoring unchanged overline color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_overline(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::OL), XCB_GC_FOREGROUND, &color);
m_colors[gc::OL] = color;
} }
return true; return true;
} }
bool renderer::on(const change_font& evt) { bool renderer::on(const signals::parser::change_font& evt) {
const uint8_t font{evt.cast()}; m_fontindex = evt.cast();
if (m_fontindex == font) {
m_log.trace_x("renderer: ignoring unchanged font index(%i)", static_cast<uint8_t>(font));
} else {
m_log.trace_x("renderer: fontindex(%i)", static_cast<uint8_t>(font));
m_fontmanager->fontindex(font);
m_fontindex = font;
}
return true; return true;
} }
bool renderer::on(const change_alignment& evt) { bool renderer::on(const signals::parser::change_alignment& evt) {
auto align = static_cast<const alignment&>(evt.cast()); auto align = static_cast<const alignment&>(evt.cast());
if (align != m_alignment) {
if (align == m_alignment) {
m_log.trace_x("renderer: ignoring unchanged alignment(%i)", static_cast<uint8_t>(align));
} else {
m_log.trace_x("renderer: set_alignment(%i)", static_cast<uint8_t>(align));
m_alignment = align; m_alignment = align;
m_currentx = 0; m_x = 0.0;
} }
return true; return true;
} }
bool renderer::on(const offset_pixel& evt) { bool renderer::on(const signals::parser::offset_pixel& evt) {
shift_content(evt.cast()); m_x += evt.cast();
return true; return true;
} }
bool renderer::on(const attribute_set& evt) { bool renderer::on(const signals::parser::attribute_set& evt) {
m_log.trace_x("renderer: attribute_set(%i, %i)", static_cast<uint8_t>(evt.cast()), true); m_attributes.set(static_cast<uint8_t>(evt.cast()), true);
m_attributes |= 1U << static_cast<uint8_t>(evt.cast());
return true; return true;
} }
bool renderer::on(const attribute_unset& evt) { bool renderer::on(const signals::parser::attribute_unset& evt) {
m_log.trace_x("renderer: attribute_unset(%i, %i)", static_cast<uint8_t>(evt.cast()), true); m_attributes.set(static_cast<uint8_t>(evt.cast()), false);
m_attributes &= ~(1U << static_cast<uint8_t>(evt.cast()));
return true; return true;
} }
bool renderer::on(const attribute_toggle& evt) { bool renderer::on(const signals::parser::attribute_toggle& evt) {
m_log.trace_x("renderer: attribute_toggle(%i)", static_cast<uint8_t>(evt.cast())); m_attributes.flip(static_cast<uint8_t>(evt.cast()));
m_attributes ^= 1U << static_cast<uint8_t>(evt.cast());
return true; return true;
} }
bool renderer::on(const action_begin& evt) { bool renderer::on(const signals::parser::action_begin& evt) {
auto a = static_cast<const action&>(evt.cast()); (void) evt;
action_block action{}; // auto a = evt.cast();
action.button = a.button; // action_block action{};
action.align = m_alignment; // action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button;
action.start_x = m_currentx; // action.align = m_alignment;
action.command = string_util::replace_all(a.command, ":", "\\:"); // action.start_x = m_x;
action.active = true; // action.command = string_util::replace_all(a.command, ":", "\\:");
// action.active = true;
if (action.button == mousebtn::NONE) { // m_actions.emplace_back(action);
action.button = mousebtn::LEFT;
}
m_log.trace_x("renderer: action_begin(%i, %s)", static_cast<uint8_t>(a.button), a.command.c_str());
m_actions.emplace_back(action);
return true; return true;
} }
bool renderer::on(const action_end& evt) { bool renderer::on(const signals::parser::action_end& evt) {
auto btn = static_cast<const mousebtn&>(evt.cast()); (void) evt;
int16_t clickable_width{0}; // auto btn = evt.cast();
// int16_t clickable_width = 0;
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) { // for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
if (!action->active || action->align != m_alignment || action->button != btn) { // if (action->active && action->align == m_alignment && action->button == btn) {
continue; // switch (action->align) {
} // case alignment::NONE:
// break;
action->active = false; // case alignment::LEFT:
// action->end_x = m_x;
switch (action->align) { // break;
case alignment::NONE: // case alignment::CENTER:
break; // clickable_width = m_x - action->start_x;
case alignment::LEFT: // action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2;
action->end_x = m_currentx; // action->end_x = action->start_x + clickable_width;
break; // break;
case alignment::CENTER: // case alignment::RIGHT:
clickable_width = m_currentx - action->start_x; // action->start_x = m_rect.width - m_x + action->start_x;
action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2; // action->end_x = m_rect.width;
action->end_x = action->start_x + clickable_width; // break;
break; // }
case alignment::RIGHT: // action->start_x += m_rect.x;
action->start_x = m_rect.width - m_currentx + action->start_x; // action->end_x += m_rect.x;
action->end_x = m_rect.width; // action->active = false;
break; // }
} // }
action->start_x += m_rect.x;
action->end_x += m_rect.x;
m_log.trace_x("renderer: action_end(%i, %s, %i)", static_cast<uint8_t>(btn), action->command, action->width());
}
return true; return true;
} }
bool renderer::on(const write_text_ascii& evt) { bool renderer::on(const signals::parser::text& evt) {
const uint16_t data[1]{evt.cast()}; auto text = evt.cast();
draw_textstring(data, 1); draw_text(text);
return true;
}
bool renderer::on(const write_text_unicode& evt) {
const uint16_t data[1]{evt.cast()};
draw_textstring(data, 1);
return true;
}
bool renderer::on(const write_text_string& evt) {
auto pkt = evt.cast();
draw_textstring(pkt.data, pkt.length);
return true; return true;
} }

View File

@ -1,70 +0,0 @@
#include <iomanip>
#include <utility>
#include "errors.hpp"
#include "utils/color.hpp"
#include "utils/string.hpp"
#include "x11/color.hpp"
POLYBAR_NS
mutex_wrapper<std::unordered_map<string, color>> g_colorstore;
const color& g_colorempty{"#00000000"};
const color& g_colorblack{"#ff000000"};
const color& g_colorwhite{"#ffffffff"};
color::color(string hex) : m_source(hex) {
if (hex.empty()) {
throw application_error("Cannot create color from empty hex");
}
m_value = std::strtoul(&hex[1], nullptr, 16);
m_color = color_util::premultiply_alpha(m_value);
}
string color::source() const {
return m_source;
}
color::operator XRenderColor() const {
XRenderColor x{};
x.red = color_util::red_channel<uint16_t>(m_color);
x.green = color_util::green_channel<uint16_t>(m_color);
x.blue = color_util::blue_channel<uint16_t>(m_color);
x.alpha = color_util::alpha_channel<uint16_t>(m_color);
return x;
}
color::operator string() const {
return color_util::hex<uint8_t>(m_color);
}
color::operator uint32_t() {
return static_cast<const color&>(*this);
}
color::operator uint32_t() const {
return m_color;
}
const color& color::parse(string input, const color& fallback) {
if (input.empty()) {
throw application_error("Cannot parse empty color");
} else if ((input = color_util::parse_hex(move(input))).empty()) {
return fallback;
}
std::lock_guard<decltype(g_colorstore)> guard(g_colorstore);
auto it = g_colorstore.find(input);
if (it == g_colorstore.end()) {
it = g_colorstore.emplace_hint(it, make_pair(input, color{input}));
}
return it->second;
}
const color& color::parse(string input) {
return parse(move(input), g_colorempty);
}
POLYBAR_NS_END

View File

@ -1,5 +1,5 @@
#include "x11/draw.hpp" #include "x11/draw.hpp"
#include "x11/connection.hpp" #include "utils/color.hpp"
POLYBAR_NS POLYBAR_NS

View File

@ -1,307 +0,0 @@
#include "x11/fonts.hpp"
#include "components/logger.hpp"
#include "errors.hpp"
#include "utils/color.hpp"
#include "utils/factory.hpp"
#include "utils/memory.hpp"
#include "x11/connection.hpp"
#include "x11/draw.hpp"
POLYBAR_NS
void font_ref::_deleter::operator()(font_ref* font) {
font->glyph_widths.clear();
font->width_lut.clear();
if (font->xft != nullptr || font->ptr != XCB_NONE) {
auto& conn = connection::make();
if (font->xft != nullptr) {
XftFontClose(conn, font->xft);
}
if (font->ptr != XCB_NONE) {
xcb_close_font(conn, font->ptr);
}
}
delete font;
}
/**
* Create instance
*/
font_manager::make_type font_manager::make() {
return factory_util::unique<font_manager>(connection::make(), logger::make());
}
font_manager::font_manager(connection& conn, const logger& logger)
: m_connection(conn)
, m_logger(logger)
, m_display(m_connection)
, m_visual(m_connection.visual())
, m_colormap(XDefaultColormap(m_display, m_connection.default_screen())) {}
font_manager::~font_manager() {
cleanup();
if (m_display) {
if (m_xftcolor_allocated) {
XftColorFree(m_display, m_visual, m_colormap, &m_xftcolor);
}
XFreeColormap(m_display, m_colormap);
}
}
void font_manager::set_visual(Visual* v) {
m_visual = v;
}
void font_manager::cleanup() {
if (m_xftdraw != nullptr) {
XftDrawDestroy(m_xftdraw);
m_xftdraw = nullptr;
}
}
bool font_manager::load(const string& name, uint8_t fontindex, int8_t offset_y) {
if (fontindex > 0 && m_fonts.find(fontindex) != m_fonts.end()) {
m_logger.warn("A font with index '%i' has already been loaded, skip...", fontindex);
return false;
} else if (fontindex == 0) {
fontindex = m_fonts.size();
m_logger.trace("font_manager: Assign font '%s' to index '%u'", name, fontindex);
} else {
m_logger.trace("font_manager: Add font '%s' to index '%u'", name, fontindex);
}
shared_ptr<font_ref> font{new font_ref{}, font_ref::deleter};
font->offset_y = offset_y;
if (open_xcb_font(font, name)) {
m_logger.info("Loaded font (xlfd=%s)", name);
} else if (font->ptr != XCB_NONE) {
m_connection.close_font_checked(font->ptr);
font->ptr = XCB_NONE;
}
if (font->ptr == XCB_NONE &&
(font->xft = XftFontOpenName(m_display, m_connection.default_screen(), name.c_str())) != nullptr) {
font->ascent = font->xft->ascent;
font->descent = font->xft->descent;
font->height = font->ascent + font->descent;
if (font->xft->pattern != nullptr) {
// XftChar8* file;
// XftPatternGetString(font->xft->pattern, "file", 0, &file);
// m_logger.info("Loaded font (pattern=%s, file=%s)", name, file);
m_logger.info("Loaded font (pattern=%s)", name);
} else {
m_logger.info("Loaded font (pattern=%s)", name);
}
}
if (font->ptr == XCB_NONE && font->xft == nullptr) {
return false;
}
m_fonts.emplace(make_pair(fontindex, move(font)));
int max_height{0};
for (auto& iter : m_fonts) {
if (iter.second->height > max_height) {
max_height = iter.second->height;
}
}
for (auto& iter : m_fonts) {
iter.second->height = max_height;
}
return true;
}
void font_manager::fontindex(uint8_t index) {
if ((m_fontindex = index) > 0) {
for (auto&& font : m_fonts) {
if (font.first == index) {
m_fontindex = index;
break;
}
}
}
}
shared_ptr<font_ref> font_manager::match_char(const uint16_t chr) {
if (!m_fonts.empty()) {
if (m_fontindex > 0 && static_cast<size_t>(m_fontindex) <= m_fonts.size()) {
auto iter = m_fonts.find(m_fontindex);
if (iter != m_fonts.end() && iter->second) {
if (has_glyph_xft(iter->second, chr)) {
return iter->second;
} else if (has_glyph_xcb(iter->second, chr)) {
return iter->second;
}
}
}
for (auto&& font : m_fonts) {
if (font.second && has_glyph_xft(font.second, chr)) {
return font.second;
} else if (font.second && has_glyph_xcb(font.second, chr)) {
return font.second;
}
}
}
return {};
}
uint8_t font_manager::glyph_width(const shared_ptr<font_ref>& font, const uint16_t chr) {
if (font && font->xft != nullptr) {
return glyph_width_xft(move(font), chr);
} else if (font && font->ptr != XCB_NONE) {
return glyph_width_xcb(move(font), chr);
} else {
return 0;
}
}
void font_manager::drawtext(const shared_ptr<font_ref>& font, xcb_pixmap_t pm, xcb_gcontext_t gc, int16_t x, int16_t y,
const uint16_t* chars, size_t num_chars) {
if (m_xftdraw == nullptr) {
m_xftdraw = XftDrawCreate(m_display, pm, m_visual, m_colormap);
}
if (font->xft != nullptr) {
XftDrawString16(m_xftdraw, &m_xftcolor, font->xft, x, y, chars, num_chars);
} else if (font->ptr != XCB_NONE) {
vector<uint16_t> ucs(num_chars);
for (size_t i = 0; i < num_chars; i++) {
ucs[i] = (chars[i] >> 8) | (chars[i] << 8);
}
xcb_poly_text_16(pm, gc, x, y, num_chars, ucs.data());
}
}
void font_manager::allocate_color(uint32_t color) {
// clang-format off
XRenderColor x{
color_util::red_channel<uint16_t>(color),
color_util::green_channel<uint16_t>(color),
color_util::blue_channel<uint16_t>(color),
color_util::alpha_channel<uint16_t>(color)};
// clang-format on
allocate_color(x);
}
void font_manager::allocate_color(XRenderColor color) {
if (m_xftcolor_allocated) {
XftColorFree(m_display, m_visual, m_colormap, &m_xftcolor);
}
if (!(m_xftcolor_allocated = XftColorAllocValue(m_display, m_visual, m_colormap, &color, &m_xftcolor))) {
m_logger.err("Failed to allocate color");
}
}
bool font_manager::open_xcb_font(const shared_ptr<font_ref>& font, string fontname) {
try {
uint32_t font_id{m_connection.generate_id()};
m_connection.open_font_checked(font_id, fontname);
m_logger.trace("Found X font '%s'", fontname);
font->ptr = font_id;
auto query = m_connection.query_font(font_id);
if (query->char_infos_len == 0) {
m_logger.warn("X font '%s' does not contain any characters... (Verify the XLFD string)", fontname);
return false;
}
font->descent = query->font_descent;
font->height = query->font_ascent + query->font_descent;
font->width = query->max_bounds.character_width;
font->char_max = query->max_byte1 << 8 | query->max_char_or_byte2;
font->char_min = query->min_byte1 << 8 | query->min_char_or_byte2;
auto chars = query.char_infos();
for (auto it = chars.begin(); it != chars.end(); it++) {
font->width_lut.emplace_back(forward<xcb_charinfo_t>(*it));
}
return true;
} catch (const std::exception& e) {
m_logger.trace("font_manager: Could not find X font '%s' (what: %s)", fontname, e.what());
}
return false;
}
uint8_t font_manager::glyph_width_xft(const shared_ptr<font_ref>& font, const uint16_t chr) {
auto it = font->glyph_widths.find(chr);
if (it != font->glyph_widths.end()) {
return it->second;
}
XGlyphInfo extents{};
FT_UInt glyph{XftCharIndex(m_display, font->xft, static_cast<FcChar32>(chr))};
XftFontLoadGlyphs(m_display, font->xft, FcFalse, &glyph, 1);
XftGlyphExtents(m_display, font->xft, &glyph, 1, &extents);
XftFontUnloadGlyphs(m_display, font->xft, &glyph, 1);
font->glyph_widths.emplace_hint(it, chr, extents.xOff); //.emplace_back(chr, extents.xOff);
return extents.xOff;
}
uint8_t font_manager::glyph_width_xcb(const shared_ptr<font_ref>& font, const uint16_t chr) {
if (!font || font->ptr == XCB_NONE) {
return 0;
} else if (static_cast<size_t>(chr - font->char_min) < font->width_lut.size()) {
return font->width_lut[chr - font->char_min].character_width;
} else {
return font->width;
}
}
bool font_manager::has_glyph_xft(const shared_ptr<font_ref>& font, const uint16_t chr) {
if (!font || font->xft == nullptr) {
return false;
} else if (XftCharExists(m_display, font->xft, static_cast<FcChar32>(chr)) == FcFalse) {
return false;
} else {
return true;
}
}
bool font_manager::has_glyph_xcb(const shared_ptr<font_ref>& font, const uint16_t chr) {
if (font->ptr == XCB_NONE) {
return false;
} else if (chr < font->char_min || chr > font->char_max) {
return false;
} else if (static_cast<size_t>(chr - font->char_min) >= font->width_lut.size()) {
return false;
} else if (font->width_lut[chr - font->char_min].character_width == 0) {
return false;
} else {
return true;
}
}
void font_manager::xcb_poly_text_16(
xcb_drawable_t d, xcb_gcontext_t gc, int16_t x, int16_t y, uint8_t len, uint16_t* str) {
static const xcb_protocol_request_t xcb_req = {5, nullptr, XCB_POLY_TEXT_16, 1};
xcb_poly_text_16_request_t req{XCB_POLY_TEXT_16, 0, len, d, gc, x, y};
uint8_t xcb_lendelta[2]{len, 0};
struct iovec xcb_parts[7]{};
xcb_parts[2].iov_base = reinterpret_cast<char*>(&req);
xcb_parts[2].iov_len = sizeof(req);
xcb_parts[3].iov_base = nullptr;
xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3;
xcb_parts[4].iov_base = xcb_lendelta;
xcb_parts[4].iov_len = sizeof(xcb_lendelta);
xcb_parts[5].iov_base = reinterpret_cast<char*>(str);
xcb_parts[5].iov_len = len * sizeof(int16_t);
xcb_parts[6].iov_base = nullptr;
xcb_parts[6].iov_len = -(xcb_parts[4].iov_len + xcb_parts[5].iov_len) & 3;
xcb_send_request(m_connection, 0, xcb_parts + 2, &xcb_req);
}
POLYBAR_NS_END

View File

@ -12,7 +12,6 @@
#include "utils/memory.hpp" #include "utils/memory.hpp"
#include "utils/process.hpp" #include "utils/process.hpp"
#include "x11/atoms.hpp" #include "x11/atoms.hpp"
#include "x11/color.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/draw.hpp" #include "x11/draw.hpp"
#include "x11/graphics.hpp" #include "x11/graphics.hpp"
@ -133,7 +132,7 @@ void tray_manager::setup(const bar_settings& bar_opts) {
} }
if (!bg.empty()) { if (!bg.empty()) {
m_opts.background = color::parse(bg, g_colorempty); m_opts.background = color_util::parse(bg);
} else { } else {
m_opts.background = bar_opts.background; m_opts.background = bar_opts.background;
} }