mirror of https://github.com/polybar/polybar.git
wip(refactor): Cairo drawing
This commit is contained in:
parent
47a2cce03d
commit
5e1886a312
|
@ -52,8 +52,8 @@ flags.append('-I'+ DirectoryOfThisScript() +'/lib/concurrentqueue/include')
|
|||
flags.append('-I'+ DirectoryOfThisScript() +'/lib/i3ipcpp/include')
|
||||
flags.append('-I'+ DirectoryOfThisScript() +'/lib/xpp/include')
|
||||
flags.append('-I'+ DirectoryOfThisScript() +'/tests')
|
||||
flags.append('-I/usr/include/freetype2')
|
||||
flags.append('-I/usr/include')
|
||||
flags.append('-I/usr/include/freetype2')
|
||||
|
||||
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
|
||||
if not working_directory:
|
||||
|
|
|
@ -56,8 +56,6 @@ if(CXXLIB_CLANG)
|
|||
elseif(CXXLIB_GCC)
|
||||
message_colored(STATUS "Linking against libstdc++" 32)
|
||||
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()
|
||||
|
||||
if(ENABLE_CCACHE)
|
||||
|
|
|
@ -50,13 +50,13 @@ option(CXXLIB_GCC "Link against stdlibc++" OFF)
|
|||
|
||||
option(BUILD_IPC_MSG "Build ipc messager" ON)
|
||||
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)
|
||||
set(DEBUG_LOGGER_TRACE OFF)
|
||||
if(DEBUG)
|
||||
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()
|
||||
|
||||
option(ENABLE_CCACHE "Enable ccache support" OFF)
|
||||
|
|
|
@ -11,69 +11,72 @@ function(colored_option message_level text var color_on color_off)
|
|||
endif()
|
||||
endfunction()
|
||||
|
||||
message(STATUS "--------------------------")
|
||||
message(STATUS " Build:")
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
message_colored(STATUS " Build type: ${CMAKE_BUILD_TYPE}" "32;1")
|
||||
message_colored(STATUS " Type: ${CMAKE_BUILD_TYPE}" "37;2")
|
||||
else()
|
||||
message_colored(STATUS " Build type: NONE" "33;1")
|
||||
message_colored(STATUS " Type: NONE" "33;1")
|
||||
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)
|
||||
if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
|
||||
message(STATUS " debug flags: ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
if(NOT DEFINED ${DEBUG_LOGGER})
|
||||
set(DEBUG_LOGGER ON)
|
||||
endif()
|
||||
if(NOT DEFINED ${ENABLE_CCACHE})
|
||||
set(ENABLE_CCACHE ON)
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
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()
|
||||
|
||||
if(CMAKE_EXE_LINKER_FLAGS)
|
||||
message(STATUS " Linker flags: ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
message_colored(STATUS " LD: ${CMAKE_EXE_LINKER_FLAGS}" "37;2")
|
||||
endif()
|
||||
|
||||
if(CXXLIB_CLANG)
|
||||
message(STATUS " C++ library: libc++")
|
||||
elseif(CXXLIB_GCC)
|
||||
message(STATUS " C++ library: libstdc++")
|
||||
else()
|
||||
message(STATUS " C++ library: system default")
|
||||
endif()
|
||||
message(STATUS " Targets:")
|
||||
colored_option(STATUS " polybar-msg" BUILD_IPC_MSG "32;1" "37;2")
|
||||
colored_option(STATUS " testsuite" BUILD_TESTS "32;1" "37;2")
|
||||
|
||||
message(STATUS "--------------------------")
|
||||
colored_option(STATUS " Build polybar-msg ${BUILD_IPC_MSG}" BUILD_IPC_MSG "32;1" "37;2")
|
||||
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 "--------------------------")
|
||||
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 "--------------------------")
|
||||
endif()
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -4,11 +4,11 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "components/types.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "events/signal_fwd.hpp"
|
||||
#include "events/signal_receiver.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "x11/events.hpp"
|
||||
#include "x11/types.hpp"
|
||||
#include "x11/window.hpp"
|
||||
|
@ -22,15 +22,14 @@ class logger;
|
|||
class parser;
|
||||
class renderer;
|
||||
class screen;
|
||||
class signal_emitter;
|
||||
class taskqueue;
|
||||
class tray_manager;
|
||||
// }}}
|
||||
|
||||
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>,
|
||||
public signal_receiver<SIGN_PRIORITY_BAR, signals::eventqueue::start, signals::ui::tick, signals::ui::shade_window, signals::ui::unshade_window,
|
||||
signals::ui::dim_window> {
|
||||
public signal_receiver<SIGN_PRIORITY_BAR, signals::eventqueue::start, signals::ui::tick,
|
||||
signals::ui::shade_window, signals::ui::unshade_window, signals::ui::dim_window> {
|
||||
public:
|
||||
using make_type = unique_ptr<bar>;
|
||||
static make_type make(bool only_initialize_values = false);
|
||||
|
|
|
@ -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,
|
||||
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:
|
||||
using make_type = unique_ptr<controller>;
|
||||
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_reload& 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::ipc::action& 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{};
|
||||
|
||||
/**
|
||||
* @brief State flag
|
||||
*/
|
||||
std::atomic<bool> m_process_events{false};
|
||||
|
||||
/**
|
||||
* @brief Controls weather the output gets printed to stdout
|
||||
*/
|
||||
|
|
|
@ -41,7 +41,7 @@ class logger {
|
|||
void trace(string message, Args... args) const {
|
||||
output(loglevel::TRACE, message, args...);
|
||||
}
|
||||
#ifdef DEBUG_LOGGER_TRACE
|
||||
#ifdef DEBUG_LOGGER_VERBOSE
|
||||
template <typename... Args>
|
||||
void trace_x(string message, Args... args) const {
|
||||
output(loglevel::TRACE, message, args...);
|
||||
|
|
|
@ -17,10 +17,6 @@ DEFINE_CHILD_ERROR(unclosed_actionblocks, parser_error);
|
|||
|
||||
class parser {
|
||||
public:
|
||||
struct packet {
|
||||
uint16_t data[128]{0U};
|
||||
size_t length{0};
|
||||
};
|
||||
using make_type = unique_ptr<parser>;
|
||||
static make_type make();
|
||||
|
||||
|
|
|
@ -1,94 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <bitset>
|
||||
|
||||
#include "cairo/fwd.hpp"
|
||||
#include "common.hpp"
|
||||
#include "components/types.hpp"
|
||||
#include "events/signal_fwd.hpp"
|
||||
#include "events/signal_receiver.hpp"
|
||||
#include "x11/extensions/fwd.hpp"
|
||||
#include "x11/fonts.hpp"
|
||||
#include "x11/types.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
// fwd
|
||||
// fwd {{{
|
||||
class connection;
|
||||
class font_manager;
|
||||
class config;
|
||||
class logger;
|
||||
class signal_emitter;
|
||||
// }}}
|
||||
|
||||
using namespace signals::parser;
|
||||
using std::map;
|
||||
|
||||
class renderer
|
||||
: public signal_receiver<SIGN_PRIORITY_RENDERER, change_background, change_foreground, change_underline,
|
||||
change_overline, change_font, change_alignment, offset_pixel, attribute_set, attribute_unset,
|
||||
attribute_toggle, action_begin, action_end, write_text_ascii, write_text_unicode, write_text_string> {
|
||||
: public signal_receiver<SIGN_PRIORITY_RENDERER, signals::parser::change_background,
|
||||
signals::parser::change_foreground, signals::parser::change_underline, signals::parser::change_overline,
|
||||
signals::parser::change_font, signals::parser::change_alignment, signals::parser::offset_pixel,
|
||||
signals::parser::attribute_set, signals::parser::attribute_unset, signals::parser::attribute_toggle,
|
||||
signals::parser::action_begin, signals::parser::action_end, signals::parser::text> {
|
||||
public:
|
||||
enum class gc : uint8_t { BG, FG, OL, UL, BT, BB, BL, BR };
|
||||
|
||||
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,
|
||||
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts);
|
||||
explicit renderer(
|
||||
connection& conn, signal_emitter& emitter, const config&, const logger& logger, const bar_settings& bar);
|
||||
~renderer();
|
||||
|
||||
renderer(const renderer& o) = delete;
|
||||
renderer& operator=(const renderer& o) = delete;
|
||||
|
||||
xcb_window_t window() const;
|
||||
const vector<action_block> actions() const;
|
||||
|
||||
void begin();
|
||||
void end();
|
||||
void flush(bool clear);
|
||||
void flush();
|
||||
|
||||
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_overline(int16_t x, uint16_t w);
|
||||
void fill_underline(int16_t x, uint16_t w);
|
||||
void fill_shift(const int16_t px);
|
||||
|
||||
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();
|
||||
void fill_overline(double x, double w);
|
||||
void fill_underline(double x, double w);
|
||||
void fill_borders();
|
||||
void draw_text(const string& contents);
|
||||
|
||||
protected:
|
||||
int16_t shift_content(int16_t x, const int16_t shift_x);
|
||||
int16_t shift_content(const int16_t shift_x);
|
||||
void adjust_clickable_areas(double width);
|
||||
void highlight_clickable_areas();
|
||||
|
||||
bool on(const change_background& evt);
|
||||
bool on(const change_foreground& evt);
|
||||
bool on(const change_underline& evt);
|
||||
bool on(const change_overline& evt);
|
||||
bool on(const change_font& evt);
|
||||
bool on(const change_alignment& evt);
|
||||
bool on(const offset_pixel& evt);
|
||||
bool on(const attribute_set& evt);
|
||||
bool on(const attribute_unset& evt);
|
||||
bool on(const attribute_toggle& evt);
|
||||
bool on(const action_begin& evt);
|
||||
bool on(const action_end& evt);
|
||||
bool on(const write_text_ascii& 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
|
||||
bool on(const signals::parser::change_background& evt);
|
||||
bool on(const signals::parser::change_foreground& evt);
|
||||
bool on(const signals::parser::change_underline& evt);
|
||||
bool on(const signals::parser::change_overline& evt);
|
||||
bool on(const signals::parser::change_font& evt);
|
||||
bool on(const signals::parser::change_alignment& evt);
|
||||
bool on(const signals::parser::offset_pixel& evt);
|
||||
bool on(const signals::parser::attribute_set& evt);
|
||||
bool on(const signals::parser::attribute_unset& evt);
|
||||
bool on(const signals::parser::attribute_toggle& evt);
|
||||
bool on(const signals::parser::action_begin& evt);
|
||||
bool on(const signals::parser::action_end& evt);
|
||||
bool on(const signals::parser::text& evt);
|
||||
|
||||
protected:
|
||||
struct reserve_area {
|
||||
|
@ -99,34 +76,45 @@ class renderer
|
|||
private:
|
||||
connection& m_connection;
|
||||
signal_emitter& m_sig;
|
||||
const config& m_conf;
|
||||
const logger& m_log;
|
||||
unique_ptr<font_manager> m_fontmanager;
|
||||
|
||||
const bar_settings& m_bar;
|
||||
|
||||
xcb_rectangle_t m_rect{0, 0, 0U, 0U};
|
||||
xcb_rectangle_t m_cleared{0, 0, 0U, 0U};
|
||||
reserve_area m_cleararea{};
|
||||
|
||||
uint8_t m_depth{32};
|
||||
xcb_window_t m_window;
|
||||
xcb_colormap_t m_colormap;
|
||||
xcb_visualtype_t* m_visual;
|
||||
// xcb_gcontext_t m_gcontext;
|
||||
xcb_gcontext_t m_gcontext;
|
||||
xcb_pixmap_t m_pixmap;
|
||||
|
||||
map<gc, xcb_gcontext_t> m_gcontexts;
|
||||
map<alignment, xcb_pixmap_t> m_pixmaps;
|
||||
vector<action_block> m_actions;
|
||||
|
||||
// bool m_autosize{false};
|
||||
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
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
|
||||
POLYBAR_NS
|
||||
|
||||
// fwd
|
||||
// fwd {{{
|
||||
struct randr_output;
|
||||
using monitor_t = shared_ptr<randr_output>;
|
||||
// }}}
|
||||
|
||||
struct enum_hash {
|
||||
template <typename T>
|
||||
|
@ -135,8 +136,9 @@ struct bar_settings {
|
|||
side_values module_margin{0U, 0U};
|
||||
edge_values strut{0U, 0U, 0U, 0U};
|
||||
|
||||
uint32_t background{0xFFFFFFFF};
|
||||
uint32_t foreground{0xFF000000};
|
||||
uint32_t background{0xFF000000};
|
||||
uint32_t foreground{0xFFFFFFFF};
|
||||
vector<uint32_t> background_steps;
|
||||
|
||||
line_settings underline{};
|
||||
line_settings overline{};
|
||||
|
|
|
@ -90,6 +90,9 @@ namespace signals {
|
|||
}
|
||||
|
||||
namespace ui {
|
||||
struct ready : public detail::base_signal<ready> {
|
||||
using base_type::base_type;
|
||||
};
|
||||
struct tick : public detail::base_signal<tick> {
|
||||
using base_type::base_type;
|
||||
};
|
||||
|
@ -153,13 +156,7 @@ namespace signals {
|
|||
struct action_end : public detail::value_signal<action_end, mousebtn> {
|
||||
using base_type::base_type;
|
||||
};
|
||||
struct write_text_ascii : public detail::value_signal<write_text_ascii, uint16_t> {
|
||||
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> {
|
||||
struct text : public detail::value_signal<text, string> {
|
||||
using base_type::base_type;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace signals {
|
|||
struct action;
|
||||
}
|
||||
namespace ui {
|
||||
struct ready;
|
||||
struct tick;
|
||||
struct button_press;
|
||||
struct visibility_change;
|
||||
|
@ -51,9 +52,7 @@ namespace signals {
|
|||
struct attribute_toggle;
|
||||
struct action_begin;
|
||||
struct action_end;
|
||||
struct write_text_ascii;
|
||||
struct write_text_unicode;
|
||||
struct write_text_string;
|
||||
struct text;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ namespace {
|
|||
inline bool operator==(uint8_t id, event_type 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
|
||||
|
|
|
@ -41,10 +41,14 @@
|
|||
|
||||
#cmakedefine XPP_EXTENSION_LIST @XPP_EXTENSION_LIST@
|
||||
|
||||
#if DEBUG
|
||||
#cmakedefine DEBUG_LOGGER
|
||||
#cmakedefine DEBUG_LOGGER_TRACE
|
||||
#cmakedefine DEBUG_LOGGER_VERBOSE
|
||||
#cmakedefine DEBUG_HINTS
|
||||
#cmakedefine DEBUG_WHITESPACE
|
||||
#cmakedefine DEBUG_SHADED
|
||||
#cmakedefine DEBUG_FONTCONFIG
|
||||
#endif
|
||||
|
||||
static const size_t EVENT_SIZE = 64;
|
||||
|
||||
|
|
|
@ -13,6 +13,11 @@ class cache {
|
|||
using map_type = std::unordered_map<KeyType, std::weak_ptr<ValueType>>;
|
||||
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>
|
||||
shared_ptr<ValueType> object(const KeyType& key, MakeArgs&&... make_args) {
|
||||
std::lock_guard<safe_map_type> guard(m_cache);
|
||||
|
@ -27,6 +32,4 @@ class cache {
|
|||
safe_map_type m_cache;
|
||||
};
|
||||
|
||||
namespace cache_util {}
|
||||
|
||||
POLYBAR_NS_END
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/cache.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
static cache<string, uint32_t> g_cache_hex;
|
||||
static cache<uint32_t, string> g_cache_colors;
|
||||
|
||||
struct rgba;
|
||||
|
||||
namespace color_util {
|
||||
template <typename T = uint8_t>
|
||||
T alpha_channel(const uint32_t value) {
|
||||
|
@ -55,21 +58,27 @@ namespace color_util {
|
|||
|
||||
template <typename T>
|
||||
string hex(uint32_t color) {
|
||||
char s[12];
|
||||
size_t len = 0;
|
||||
string hex;
|
||||
|
||||
uint8_t a = alpha_channel<T>(color);
|
||||
uint8_t r = red_channel<T>(color);
|
||||
uint8_t g = green_channel<T>(color);
|
||||
uint8_t b = blue_channel<T>(color);
|
||||
if (!g_cache_hex.check(color)) {
|
||||
char s[12];
|
||||
size_t len = 0;
|
||||
|
||||
if (std::is_same<T, uint16_t>::value) {
|
||||
len = snprintf(s, sizeof(s), "#%02x%02x%02x%02x", a, r, g, b);
|
||||
} else if (std::is_same<T, uint8_t>::value) {
|
||||
len = snprintf(s, sizeof(s), "#%02x%02x%02x", r, g, b);
|
||||
uint8_t a = alpha_channel<T>(color);
|
||||
uint8_t r = red_channel<T>(color);
|
||||
uint8_t g = green_channel<T>(color);
|
||||
uint8_t b = blue_channel<T>(color);
|
||||
|
||||
if (std::is_same<T, uint16_t>::value) {
|
||||
len = snprintf(s, sizeof(s), "#%02x%02x%02x%02x", a, r, g, b);
|
||||
} else if (std::is_same<T, uint8_t>::value) {
|
||||
len = snprintf(s, sizeof(s), "#%02x%02x%02x", r, g, b);
|
||||
}
|
||||
|
||||
hex = string(s, len);
|
||||
}
|
||||
|
||||
return string{s, 0, len};
|
||||
return *g_cache_hex.object(color, 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
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace math_util {
|
|||
*/
|
||||
template <typename ValueType>
|
||||
ValueType min(ValueType one, ValueType two) {
|
||||
return one < two ? one : two;
|
||||
return one < two ? one : two;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ namespace math_util {
|
|||
*/
|
||||
template <typename ValueType>
|
||||
ValueType max(ValueType one, ValueType two) {
|
||||
return one > two ? one : two;
|
||||
return one > two ? one : two;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,6 +87,10 @@ namespace math_util {
|
|||
ReturnType nearest_5(double value) {
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -8,20 +8,21 @@ list(REMOVE_ITEM SOURCES ipc.cpp)
|
|||
# Locate dependencies {{{
|
||||
|
||||
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)
|
||||
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} ${X11_Xft_LIB})
|
||||
#set(APP_LIBRARIES ${APP_LIBRARIES} ${FONTCONFIG_LIBRARIES})
|
||||
# set(APP_LIBRARIES ${APP_LIBRARIES} ${PANGOCAIRO_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}/lib/concurrentqueue/include)
|
||||
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
|
||||
set(XCB_PROTOS xproto)
|
||||
|
@ -147,7 +148,6 @@ target_link_libraries(${PROJECT_NAME} Threads::Threads)
|
|||
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC
|
||||
$<$<CXX_COMPILER_ID:GNU>:$<$<CONFIG:MinSizeRel>:-flto>>
|
||||
${X11_Xft_DEFINITIONS}
|
||||
${X11_XCB_DEFINITIONS}
|
||||
${XCB_DEFINITIONS})
|
||||
|
||||
|
|
|
@ -182,18 +182,32 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
|
|||
|
||||
const auto parse_or_throw = [&](string key, string def) {
|
||||
try {
|
||||
return color::parse(m_conf.get(bs, key, def));
|
||||
return color_util::parse(m_conf.get(bs, key, def));
|
||||
} catch (const exception& err) {
|
||||
throw application_error(sstream() << "Failed to set " << key << " (reason: " << err.what() << ")");
|
||||
}
|
||||
};
|
||||
|
||||
// Load foreground/background
|
||||
m_opts.background = parse_or_throw("background", color_util::hex<uint16_t>(m_opts.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));
|
||||
}
|
||||
|
||||
// Load 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)
|
||||
auto line_color = parse_or_throw("line-color", "#f00"s);
|
||||
// Load over-/underline
|
||||
auto line_color = m_conf.get(bs, "line-color", "#f00"s);
|
||||
auto line_size = m_conf.get(bs, "line-size", 0);
|
||||
|
||||
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);
|
||||
|
||||
// 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_color = m_conf.get(bs, "border-color", "#00000000"s);
|
||||
|
||||
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);
|
||||
|
@ -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) + ")");
|
||||
}
|
||||
|
||||
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.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.center.y = m_opts.size.h;
|
||||
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 += m_opts.borders[edge::LEFT].size;
|
||||
|
||||
m_log.trace("bar: Create renderer");
|
||||
auto fonts = m_conf.get_list(m_conf.section(), "font", {});
|
||||
m_renderer = renderer::make(m_opts, move(fonts));
|
||||
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: Attaching sink to registry");
|
||||
m_log.trace("bar: Attach X event sink");
|
||||
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_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_log.trace("bar: Attach signal receiver");
|
||||
m_sig.attach(this);
|
||||
}
|
||||
|
||||
|
@ -354,8 +339,6 @@ void bar::parse(string&& data, bool force) {
|
|||
}
|
||||
}
|
||||
|
||||
m_renderer->fill_background();
|
||||
|
||||
try {
|
||||
m_parser->parse(settings(), data);
|
||||
} catch (const parser_error& err) {
|
||||
|
@ -365,7 +348,7 @@ void bar::parse(string&& data, bool force) {
|
|||
m_renderer->end();
|
||||
|
||||
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)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -510,7 +493,7 @@ void bar::handle(const evt::destroy_notify& evt) {
|
|||
* _NET_WM_WINDOW_OPACITY atom value
|
||||
*/
|
||||
void bar::handle(const evt::enter_notify&) {
|
||||
#if DEBUG
|
||||
#ifdef DEBUG_SHADED
|
||||
if (m_opts.origin == edge::TOP) {
|
||||
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::unshade_window{}); });
|
||||
return;
|
||||
|
@ -534,7 +517,7 @@ void bar::handle(const evt::enter_notify&) {
|
|||
* _NET_WM_WINDOW_OPACITY atom value
|
||||
*/
|
||||
void bar::handle(const evt::leave_notify&) {
|
||||
#if DEBUG
|
||||
#ifdef DEBUG_SHADED
|
||||
if (m_opts.origin == edge::TOP) {
|
||||
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::shade_window{}); });
|
||||
return;
|
||||
|
@ -571,7 +554,7 @@ void bar::handle(const evt::button_press& evt) {
|
|||
m_buttonpress_pos = evt->event_x;
|
||||
|
||||
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)) {
|
||||
m_log.trace("Found matching input area");
|
||||
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_renderer->flush(false);
|
||||
m_renderer->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,7 +626,7 @@ void bar::handle(const evt::expose& evt) {
|
|||
* pseudo-transparent background when it changes
|
||||
*/
|
||||
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();
|
||||
m_log.trace_x("bar: property_notify(%s)", atom_name);
|
||||
#endif
|
||||
|
@ -654,9 +637,42 @@ void bar::handle(const evt::property_notify& evt) {
|
|||
}
|
||||
|
||||
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_tray->setup(static_cast<const bar_settings&>(m_opts));
|
||||
|
||||
broadcast_visibility();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -677,7 +693,7 @@ bool bar::on(const signals::ui::unshade_window&) {
|
|||
m_sig.emit(signals::ui::tick{});
|
||||
}
|
||||
if (!remaining) {
|
||||
m_renderer->flush(false);
|
||||
m_renderer->flush();
|
||||
}
|
||||
if (m_opts.dimmed) {
|
||||
m_opts.dimmed = false;
|
||||
|
@ -716,7 +732,7 @@ bool bar::on(const signals::ui::shade_window&) {
|
|||
m_sig.emit(signals::ui::tick{});
|
||||
}
|
||||
if (!remaining) {
|
||||
m_renderer->flush(false);
|
||||
m_renderer->flush();
|
||||
}
|
||||
if (!m_opts.dimmed) {
|
||||
m_opts.dimmed = true;
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
#include <fstream>
|
||||
|
||||
#include "components/config.hpp"
|
||||
#include "utils/color.hpp"
|
||||
#include "utils/env.hpp"
|
||||
#include "utils/factory.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/math.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "x11/color.hpp"
|
||||
#include "x11/xresources.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
|
@ -276,8 +275,15 @@ chrono::duration<double> config::convert(string&& value) const {
|
|||
}
|
||||
|
||||
template <>
|
||||
color config::convert(string&& value) const {
|
||||
return color{forward<string>(value)};
|
||||
rgba config::convert(string&& value) const {
|
||||
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
|
||||
|
|
|
@ -148,11 +148,8 @@ controller::~controller() {
|
|||
*/
|
||||
bool controller::run(bool writeback) {
|
||||
m_log.info("Starting application");
|
||||
|
||||
assert(!m_connection.connection_has_error());
|
||||
|
||||
m_writeback = writeback;
|
||||
|
||||
m_sig.attach(this);
|
||||
|
||||
size_t started_modules{0};
|
||||
|
@ -183,10 +180,7 @@ bool controller::run(bool writeback) {
|
|||
throw application_error("No modules started");
|
||||
}
|
||||
|
||||
m_sig.emit(signals::eventqueue::start{});
|
||||
|
||||
m_connection.flush();
|
||||
|
||||
m_event_thread = thread(&controller::process_eventqueue, this);
|
||||
|
||||
read_events();
|
||||
|
@ -205,6 +199,9 @@ bool controller::run(bool writeback) {
|
|||
* Enqueue event
|
||||
*/
|
||||
bool controller::enqueue(event&& evt) {
|
||||
if (!m_process_events && evt.type != event_type::QUIT) {
|
||||
return false;
|
||||
}
|
||||
if (!m_queue.enqueue(forward<decltype(evt)>(evt))) {
|
||||
m_log.warn("Failed to enqueue event");
|
||||
return false;
|
||||
|
@ -330,8 +327,7 @@ void controller::read_events() {
|
|||
*/
|
||||
void controller::process_eventqueue() {
|
||||
m_log.info("Eventqueue worker (thread-id=%lu)", this_thread::get_id());
|
||||
|
||||
enqueue(make_update_evt(true));
|
||||
m_sig.emit(signals::eventqueue::start{});
|
||||
|
||||
while (!g_terminate) {
|
||||
event evt{};
|
||||
|
@ -556,6 +552,16 @@ bool controller::on(const signals::eventqueue::check_state&) {
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "components/logger.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "utils/concurrency.hpp"
|
||||
#include "utils/factory.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
@ -60,9 +61,9 @@ logger::logger(loglevel level) : m_level(level) {
|
|||
* Set output verbosity
|
||||
*/
|
||||
void logger::verbosity(loglevel&& level) {
|
||||
#ifndef DEBUG
|
||||
#ifndef DEBUG_LOGGER
|
||||
if (level == loglevel::TRACE) {
|
||||
throw application_error("Trace logging is only enabled for debug builds...");
|
||||
throw application_error("Trace logging is not enabled...");
|
||||
}
|
||||
#endif
|
||||
m_level = forward<decltype(level)>(level);
|
||||
|
|
|
@ -173,60 +173,8 @@ size_t parser::text(string&& data) {
|
|||
}
|
||||
#endif
|
||||
|
||||
const uint8_t* utf{reinterpret_cast<const uint8_t*>(&data[0])};
|
||||
|
||||
// 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;
|
||||
m_sig.emit(signals::parser::text{forward<string>(data)});
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
#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 "errors.hpp"
|
||||
#include "events/signal.hpp"
|
||||
#include "events/signal_emitter.hpp"
|
||||
#include "events/signal_receiver.hpp"
|
||||
#include "utils/color.hpp"
|
||||
#include "utils/factory.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/math.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "x11/atoms.hpp"
|
||||
#include "x11/connection.hpp"
|
||||
#include "x11/draw.hpp"
|
||||
|
@ -18,53 +26,50 @@ POLYBAR_NS
|
|||
/**
|
||||
* 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
|
||||
return factory_util::unique<renderer>(
|
||||
connection::make(),
|
||||
signal_emitter::make(),
|
||||
config::make(),
|
||||
logger::make(),
|
||||
font_manager::make(),
|
||||
forward<decltype(bar)>(bar),
|
||||
forward<decltype(fonts)>(fonts));
|
||||
forward<decltype(bar)>(bar));
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct renderer instance
|
||||
*/
|
||||
renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logger,
|
||||
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts)
|
||||
renderer::renderer(
|
||||
connection& conn, signal_emitter& sig, const config& conf, const logger& logger, const bar_settings& bar)
|
||||
: m_connection(conn)
|
||||
, m_sig(emitter)
|
||||
, m_sig(sig)
|
||||
, m_conf(conf)
|
||||
, m_log(logger)
|
||||
, m_fontmanager(forward<decltype(font_manager)>(font_manager))
|
||||
, m_bar(forward<const bar_settings&>(bar))
|
||||
, m_rect(m_bar.inner_area()) {
|
||||
m_sig.attach(this);
|
||||
|
||||
m_log.trace("renderer: Get TrueColor visual");
|
||||
{
|
||||
if ((m_visual = m_connection.visual_type(m_connection.screen(), 32)) == nullptr) {
|
||||
m_log.err("No 32-bit TrueColor visual found...");
|
||||
|
||||
if ((m_visual = m_connection.visual_type(m_connection.screen(), 32)) == nullptr) {
|
||||
m_log.err("No 32-bit TrueColor visual found...");
|
||||
|
||||
if ((m_visual = m_connection.visual_type(m_connection.screen(), 24)) == nullptr) {
|
||||
m_log.err("No 24-bit TrueColor visual found, aborting...");
|
||||
throw application_error("No matching TrueColor visual found...");
|
||||
if ((m_visual = m_connection.visual_type(m_connection.screen(), 24)) == nullptr) {
|
||||
m_log.err("No 24-bit TrueColor visual found...");
|
||||
} else {
|
||||
m_depth = 24;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_visual == nullptr) {
|
||||
throw application_error("No matching TrueColor visual found...");
|
||||
throw application_error("No matching TrueColor");
|
||||
}
|
||||
|
||||
m_depth = 24;
|
||||
|
||||
m_fontmanager->set_visual(m_connection.visual(m_depth));
|
||||
}
|
||||
|
||||
m_log.trace("renderer: Allocate colormap");
|
||||
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_colormap = m_connection.generate_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");
|
||||
{
|
||||
|
@ -88,73 +93,61 @@ renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logg
|
|||
}
|
||||
|
||||
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
|
||||
vector<uint32_t> colors {
|
||||
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
|
||||
m_pixmap = m_connection.generate_id();
|
||||
m_connection.create_pixmap(m_depth, m_pixmap, m_window, m_bar.size.w, m_bar.size.h);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint32_t mask{0};
|
||||
uint32_t value_list[32]{0};
|
||||
m_log.trace("renderer: Allocate graphic context");
|
||||
{
|
||||
uint32_t mask{0};
|
||||
uint32_t value_list[32]{0};
|
||||
xcb_params_gc_t params{};
|
||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, foreground, m_bar.foreground);
|
||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 0);
|
||||
connection::pack_values(mask, ¶ms, value_list);
|
||||
m_gcontext = m_connection.generate_id();
|
||||
m_connection.create_gc(m_gcontext, m_pixmap, mask, value_list);
|
||||
}
|
||||
|
||||
xcb_params_gc_t params{};
|
||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, foreground, colors[i]);
|
||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 0);
|
||||
connection::pack_values(mask, ¶ms, value_list);
|
||||
|
||||
m_colors.emplace(gc(i), colors[i]);
|
||||
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");
|
||||
{
|
||||
auto fonts_loaded = false;
|
||||
auto fontindex = 0;
|
||||
|
||||
auto fonts = m_conf.get_list<string>(m_conf.section(), "font", {});
|
||||
if (fonts.empty()) {
|
||||
m_log.warn("No fonts specified, using fallback font \"fixed\"");
|
||||
fonts.emplace_back("fixed");
|
||||
}
|
||||
|
||||
auto fonts_loaded = false;
|
||||
for (const auto& f : fonts) {
|
||||
fontindex++;
|
||||
vector<string> fd{string_util::split(f, ';')};
|
||||
string pattern{fd[0]};
|
||||
int offset{0};
|
||||
|
||||
if (fd.size() > 1) {
|
||||
offset = std::stoi(fd[1], nullptr, 10);
|
||||
}
|
||||
|
||||
if (m_fontmanager->load(pattern, fontindex, offset)) {
|
||||
fonts_loaded = true;
|
||||
} else {
|
||||
m_log.warn("Unable to load font '%s'", fd[0]);
|
||||
}
|
||||
auto font = cairo::make_font(*m_context, string(fd[0]), fd.size() > 1 ? std::atoi(fd[1].c_str()) : 0);
|
||||
m_log.info("Loaded font \"%s\" (name=%s, file=%s)", fd[0], font->name(), font->file());
|
||||
*m_context << move(font);
|
||||
fonts_loaded = true;
|
||||
}
|
||||
|
||||
if (!fonts_loaded && !fonts.empty()) {
|
||||
m_log.warn("Unable to load fonts, using fallback font \"fixed\"");
|
||||
}
|
||||
if (!fonts_loaded && !m_fontmanager->load("fixed")) {
|
||||
if (!fonts_loaded) {
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completed action blocks
|
||||
*/
|
||||
const vector<action_block> renderer::actions() const {
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin render routine
|
||||
*/
|
||||
void renderer::begin() {
|
||||
m_log.trace_x("renderer: begin");
|
||||
|
||||
m_rect = m_bar.inner_area();
|
||||
m_alignment = alignment::NONE;
|
||||
m_currentx = 0;
|
||||
m_attributes = 0;
|
||||
// Reset state
|
||||
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() {
|
||||
m_log.trace_x("renderer: end");
|
||||
|
||||
m_fontmanager->cleanup();
|
||||
highlight_clickable_areas();
|
||||
|
||||
#ifdef DEBUG_HINTS
|
||||
debug_hints();
|
||||
#endif
|
||||
m_context->restore();
|
||||
m_surface->flush();
|
||||
|
||||
flush(false);
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush pixmap contents onto the target window
|
||||
*/
|
||||
void renderer::flush(bool clear) {
|
||||
const xcb_rectangle_t& r = m_rect;
|
||||
void renderer::flush() {
|
||||
m_log.trace_x("renderer: flush");
|
||||
|
||||
xcb_rectangle_t top{0, 0, 0U, 0U};
|
||||
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
|
||||
#ifdef DEBUG_SHADED
|
||||
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 x1 = 0;
|
||||
auto y1 = r.height - m_bar.shade_size.h - r.y - geom->height;
|
||||
auto x2 = r.x;
|
||||
auto y2 = r.y;
|
||||
auto w = r.width;
|
||||
auto h = r.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);
|
||||
auto y1 = m_rect.height - m_bar.shade_size.h - m_rect.y - geom->height;
|
||||
auto x2 = m_rect.x;
|
||||
auto y2 = m_rect.y;
|
||||
auto w = m_rect.width;
|
||||
auto h = m_rect.height - m_bar.shade_size.h + geom->height;
|
||||
m_connection.copy_area(m_pixmap, m_window, m_gcontext, x1, y1, x2, y2, w, h);
|
||||
m_connection.flush();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_log.trace("renderer: copy pixmap (clear=%i, geom=%dx%d+%d+%d)", clear, r.width, r.height, r.x, r.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_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_log.trace_x("renderer: copy pixmap (geom=%dx%d+%d+%d)", m_rect.width, m_rect.height, m_rect.x, m_rect.y);
|
||||
m_connection.copy_area(m_pixmap, m_window, m_gcontext, 0, 0, 0, 0, m_bar.size.w, m_bar.size.h);
|
||||
m_connection.flush();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
void renderer::fill_background() {
|
||||
m_log.trace_x("renderer: fill_background");
|
||||
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), 0, 0, m_rect.width, m_rect.height);
|
||||
m_context->save();
|
||||
*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
|
||||
*/
|
||||
void renderer::fill_overline(int16_t x, uint16_t w) {
|
||||
if (!check_attribute(attribute::OVERLINE)) {
|
||||
return m_log.trace_x("renderer: not filling overline (flag unset)");
|
||||
} else if (!m_bar.overline.size) {
|
||||
return m_log.trace_x("renderer: not filling overline (size=0)");
|
||||
void renderer::fill_overline(double x, double w) {
|
||||
if (m_bar.overline.size && m_attributes.test(static_cast<uint8_t>(attribute::OVERLINE))) {
|
||||
m_log.trace_x("renderer: overline(x=%i, w=%i)", x, w);
|
||||
m_context->save();
|
||||
*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
|
||||
*/
|
||||
void renderer::fill_underline(int16_t x, uint16_t w) {
|
||||
if (!check_attribute(attribute::UNDERLINE)) {
|
||||
return m_log.trace_x("renderer: not filling underline (flag unset)");
|
||||
} else if (!m_bar.underline.size) {
|
||||
return m_log.trace_x("renderer: not filling underline (size=0)");
|
||||
}
|
||||
m_log.trace_x("renderer: fill_underline(%i, #%08x)", m_bar.underline.size, m_colors[gc::UL]);
|
||||
int16_t y{static_cast<int16_t>(m_rect.height - m_bar.underline.size)};
|
||||
draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x, y, w, m_bar.underline.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
void renderer::fill_underline(double x, double w) {
|
||||
if (m_bar.underline.size && m_attributes.test(static_cast<uint8_t>(attribute::UNDERLINE))) {
|
||||
m_log.trace_x("renderer: underline(x=%i, w=%i)", x, w);
|
||||
m_context->save();
|
||||
*m_context << m_compositing_underline;
|
||||
*m_context << m_color_underline;
|
||||
*m_context << cairo::rect{x, static_cast<double>(m_rect.y + m_rect.height - m_bar.underline.size), w,
|
||||
static_cast<double>(m_bar.underline.size)};
|
||||
m_context->fill();
|
||||
m_context->restore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completed action blocks
|
||||
* Fill border colors
|
||||
*/
|
||||
const vector<action_block> renderer::get_actions() {
|
||||
return m_actions;
|
||||
void renderer::fill_borders() {
|
||||
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) {
|
||||
if (x > m_rect.width) {
|
||||
return m_rect.width;
|
||||
} else if (x < 0) {
|
||||
return 0;
|
||||
void renderer::draw_text(const string& contents) {
|
||||
m_log.trace_x("renderer: text(%s)", contents.c_str());
|
||||
|
||||
cairo_text_extents_t extents;
|
||||
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};
|
||||
double delta{0.0};
|
||||
|
||||
switch (m_alignment) {
|
||||
case alignment::NONE:
|
||||
break;
|
||||
case alignment::LEFT:
|
||||
break;
|
||||
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;
|
||||
if (m_alignment == alignment::CENTER) {
|
||||
origin.x += m_rect.width / 2.0 - extents.width / 2.0;
|
||||
adjust_clickable_areas(extents.width / 2.0);
|
||||
} else if (m_alignment == alignment::RIGHT) {
|
||||
origin.x += m_rect.width - extents.width;
|
||||
adjust_clickable_areas(extents.width);
|
||||
} else {
|
||||
origin.x += m_x;
|
||||
}
|
||||
|
||||
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
|
||||
if (m_alignment != alignment::LEFT) {
|
||||
for (auto&& action : m_actions) {
|
||||
if (action.active || action.align != m_alignment) {
|
||||
continue;
|
||||
}
|
||||
origin.y += m_rect.height / 2.0;
|
||||
|
||||
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) {
|
||||
if (!action.active && action.align == m_alignment) {
|
||||
action.start_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
|
||||
*/
|
||||
void renderer::debug_hints() {
|
||||
uint16_t border_width{1};
|
||||
map<alignment, int> hint_num{{
|
||||
// clang-format off
|
||||
{alignment::LEFT, 0},
|
||||
{alignment::CENTER, 0},
|
||||
{alignment::RIGHT, 0},
|
||||
// clang-format on
|
||||
}};
|
||||
|
||||
m_debughints.clear();
|
||||
|
||||
void renderer::highlight_clickable_areas() {
|
||||
#ifdef DEBUG_HINTS
|
||||
map<alignment, int> hint_num{};
|
||||
for (auto&& action : m_actions) {
|
||||
if (action.active) {
|
||||
continue;
|
||||
if (!action.active) {
|
||||
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;
|
||||
|
||||
m_context->save();
|
||||
*m_context << CAIRO_OPERATOR_OVERLAY << (n % 2 ? 0x55FF0000 : 0x5500FF00);
|
||||
*m_context << cairo::rect{x, y, w, h};
|
||||
m_context->fill();
|
||||
m_context->restore();
|
||||
}
|
||||
|
||||
xcb_window_t hintwin{m_connection.generate_id()};
|
||||
m_debughints.emplace_back(hintwin);
|
||||
|
||||
uint8_t num{static_cast<uint8_t>(hint_num.find(action.align)->second++)};
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
bool renderer::on(const change_background& evt) {
|
||||
bool renderer::on(const signals::parser::change_background& evt) {
|
||||
const uint32_t color{evt.cast()};
|
||||
|
||||
if (m_colors[gc::BG] == 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);
|
||||
if (color != m_color_background) {
|
||||
m_color_background = color;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const change_foreground& evt) {
|
||||
bool renderer::on(const signals::parser::change_foreground& evt) {
|
||||
const uint32_t color{evt.cast()};
|
||||
|
||||
if (m_colors[gc::FG] == 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;
|
||||
if (color != m_color_foreground) {
|
||||
m_color_foreground = color;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const change_underline& evt) {
|
||||
bool renderer::on(const signals::parser::change_underline& evt) {
|
||||
const uint32_t color{evt.cast()};
|
||||
|
||||
if (m_colors[gc::UL] == 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;
|
||||
if (color != m_color_underline) {
|
||||
m_color_underline = color;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const change_overline& evt) {
|
||||
bool renderer::on(const signals::parser::change_overline& evt) {
|
||||
const uint32_t color{evt.cast()};
|
||||
|
||||
if (m_colors[gc::OL] == 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;
|
||||
if (color != m_color_overline) {
|
||||
m_color_overline = color;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const change_font& evt) {
|
||||
const uint8_t font{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;
|
||||
}
|
||||
|
||||
bool renderer::on(const signals::parser::change_font& evt) {
|
||||
m_fontindex = evt.cast();
|
||||
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());
|
||||
|
||||
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));
|
||||
if (align != m_alignment) {
|
||||
m_alignment = align;
|
||||
m_currentx = 0;
|
||||
m_x = 0.0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const offset_pixel& evt) {
|
||||
shift_content(evt.cast());
|
||||
bool renderer::on(const signals::parser::offset_pixel& evt) {
|
||||
m_x += evt.cast();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const attribute_set& evt) {
|
||||
m_log.trace_x("renderer: attribute_set(%i, %i)", static_cast<uint8_t>(evt.cast()), true);
|
||||
m_attributes |= 1U << static_cast<uint8_t>(evt.cast());
|
||||
bool renderer::on(const signals::parser::attribute_set& evt) {
|
||||
m_attributes.set(static_cast<uint8_t>(evt.cast()), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const attribute_unset& evt) {
|
||||
m_log.trace_x("renderer: attribute_unset(%i, %i)", static_cast<uint8_t>(evt.cast()), true);
|
||||
m_attributes &= ~(1U << static_cast<uint8_t>(evt.cast()));
|
||||
bool renderer::on(const signals::parser::attribute_unset& evt) {
|
||||
m_attributes.set(static_cast<uint8_t>(evt.cast()), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const attribute_toggle& evt) {
|
||||
m_log.trace_x("renderer: attribute_toggle(%i)", static_cast<uint8_t>(evt.cast()));
|
||||
m_attributes ^= 1U << static_cast<uint8_t>(evt.cast());
|
||||
bool renderer::on(const signals::parser::attribute_toggle& evt) {
|
||||
m_attributes.flip(static_cast<uint8_t>(evt.cast()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const action_begin& evt) {
|
||||
auto a = static_cast<const action&>(evt.cast());
|
||||
action_block action{};
|
||||
action.button = a.button;
|
||||
action.align = m_alignment;
|
||||
action.start_x = m_currentx;
|
||||
action.command = string_util::replace_all(a.command, ":", "\\:");
|
||||
action.active = true;
|
||||
|
||||
if (action.button == mousebtn::NONE) {
|
||||
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);
|
||||
|
||||
bool renderer::on(const signals::parser::action_begin& evt) {
|
||||
(void) evt;
|
||||
// auto a = evt.cast();
|
||||
// action_block action{};
|
||||
// action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button;
|
||||
// action.align = m_alignment;
|
||||
// action.start_x = m_x;
|
||||
// action.command = string_util::replace_all(a.command, ":", "\\:");
|
||||
// action.active = true;
|
||||
// m_actions.emplace_back(action);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const action_end& evt) {
|
||||
auto btn = static_cast<const mousebtn&>(evt.cast());
|
||||
int16_t clickable_width{0};
|
||||
|
||||
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
|
||||
if (!action->active || action->align != m_alignment || action->button != btn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
action->active = false;
|
||||
|
||||
switch (action->align) {
|
||||
case alignment::NONE:
|
||||
break;
|
||||
case alignment::LEFT:
|
||||
action->end_x = m_currentx;
|
||||
break;
|
||||
case alignment::CENTER:
|
||||
clickable_width = m_currentx - action->start_x;
|
||||
action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2;
|
||||
action->end_x = action->start_x + clickable_width;
|
||||
break;
|
||||
case alignment::RIGHT:
|
||||
action->start_x = m_rect.width - m_currentx + action->start_x;
|
||||
action->end_x = m_rect.width;
|
||||
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());
|
||||
}
|
||||
|
||||
bool renderer::on(const signals::parser::action_end& evt) {
|
||||
(void) evt;
|
||||
// auto btn = evt.cast();
|
||||
// int16_t clickable_width = 0;
|
||||
// for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
|
||||
// if (action->active && action->align == m_alignment && action->button == btn) {
|
||||
// switch (action->align) {
|
||||
// case alignment::NONE:
|
||||
// break;
|
||||
// case alignment::LEFT:
|
||||
// action->end_x = m_x;
|
||||
// break;
|
||||
// case alignment::CENTER:
|
||||
// clickable_width = m_x - action->start_x;
|
||||
// action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2;
|
||||
// action->end_x = action->start_x + clickable_width;
|
||||
// break;
|
||||
// case alignment::RIGHT:
|
||||
// action->start_x = m_rect.width - m_x + action->start_x;
|
||||
// action->end_x = m_rect.width;
|
||||
// break;
|
||||
// }
|
||||
// action->start_x += m_rect.x;
|
||||
// action->end_x += m_rect.x;
|
||||
// action->active = false;
|
||||
// }
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool renderer::on(const write_text_ascii& evt) {
|
||||
const uint16_t data[1]{evt.cast()};
|
||||
draw_textstring(data, 1);
|
||||
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);
|
||||
bool renderer::on(const signals::parser::text& evt) {
|
||||
auto text = evt.cast();
|
||||
draw_text(text);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -1,5 +1,5 @@
|
|||
#include "x11/draw.hpp"
|
||||
#include "x11/connection.hpp"
|
||||
#include "utils/color.hpp"
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
|
|
|
@ -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
|
|
@ -12,7 +12,6 @@
|
|||
#include "utils/memory.hpp"
|
||||
#include "utils/process.hpp"
|
||||
#include "x11/atoms.hpp"
|
||||
#include "x11/color.hpp"
|
||||
#include "x11/connection.hpp"
|
||||
#include "x11/draw.hpp"
|
||||
#include "x11/graphics.hpp"
|
||||
|
@ -133,7 +132,7 @@ void tray_manager::setup(const bar_settings& bar_opts) {
|
|||
}
|
||||
|
||||
if (!bg.empty()) {
|
||||
m_opts.background = color::parse(bg, g_colorempty);
|
||||
m_opts.background = color_util::parse(bg);
|
||||
} else {
|
||||
m_opts.background = bar_opts.background;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue