feat(render): support pseudo-transparency
This adds pseudo-transparency for the background of the bar and the background of the systray.
This commit is contained in:
parent
654c667698
commit
3de914abca
@ -243,7 +243,7 @@ namespace cairo {
|
|||||||
/**
|
/**
|
||||||
* Match and create font from given fontconfig pattern
|
* Match and create font from given fontconfig pattern
|
||||||
*/
|
*/
|
||||||
decltype(auto) make_font(cairo_t* cairo, string&& fontname, double offset, double dpi_x, double dpi_y) {
|
inline decltype(auto) make_font(cairo_t* cairo, string&& fontname, double offset, double dpi_x, double dpi_y) {
|
||||||
static bool fc_init{false};
|
static bool fc_init{false};
|
||||||
if (!fc_init && !(fc_init = FcInit())) {
|
if (!fc_init && !(fc_init = FcInit())) {
|
||||||
throw application_error("Could not load fontconfig");
|
throw application_error("Could not load fontconfig");
|
||||||
|
@ -35,7 +35,8 @@ using modulemap_t = std::map<alignment, vector<module_t>>;
|
|||||||
|
|
||||||
class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eventqueue::exit_terminate, signals::eventqueue::exit_reload,
|
class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eventqueue::exit_terminate, signals::eventqueue::exit_reload,
|
||||||
signals::eventqueue::notify_change, signals::eventqueue::notify_forcechange, signals::eventqueue::check_state, signals::ipc::action,
|
signals::eventqueue::notify_change, signals::eventqueue::notify_forcechange, signals::eventqueue::check_state, signals::ipc::action,
|
||||||
signals::ipc::command, signals::ipc::hook, signals::ui::ready, signals::ui::button_press> {
|
signals::ipc::command, signals::ipc::hook, signals::ui::ready, signals::ui::button_press,
|
||||||
|
signals::ui::update_background> {
|
||||||
public:
|
public:
|
||||||
using make_type = unique_ptr<controller>;
|
using make_type = unique_ptr<controller>;
|
||||||
static make_type make(unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& config_watch);
|
static make_type make(unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& config_watch);
|
||||||
@ -65,6 +66,7 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
|
|||||||
bool on(const signals::ipc::action& evt);
|
bool on(const signals::ipc::action& evt);
|
||||||
bool on(const signals::ipc::command& evt);
|
bool on(const signals::ipc::command& evt);
|
||||||
bool on(const signals::ipc::hook& evt);
|
bool on(const signals::ipc::hook& evt);
|
||||||
|
bool on(const signals::ui::update_background& evt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
connection& m_connection;
|
connection& m_connection;
|
||||||
|
@ -49,7 +49,7 @@ class ipc {
|
|||||||
const logger& m_log;
|
const logger& m_log;
|
||||||
|
|
||||||
string m_path{};
|
string m_path{};
|
||||||
unique_ptr<file_descriptor> m_fd{};
|
unique_ptr<file_descriptor> m_fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
POLYBAR_NS_END
|
POLYBAR_NS_END
|
||||||
|
@ -17,6 +17,7 @@ POLYBAR_NS
|
|||||||
class connection;
|
class connection;
|
||||||
class config;
|
class config;
|
||||||
class logger;
|
class logger;
|
||||||
|
class background_manager;
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
using std::map;
|
using std::map;
|
||||||
@ -39,7 +40,7 @@ class renderer
|
|||||||
static make_type make(const bar_settings& bar);
|
static make_type make(const bar_settings& bar);
|
||||||
|
|
||||||
explicit renderer(
|
explicit renderer(
|
||||||
connection& conn, signal_emitter& sig, const config&, const logger& logger, const bar_settings& bar);
|
connection& conn, signal_emitter& sig, const config&, const logger& logger, const bar_settings& bar, background_manager& background_manager);
|
||||||
~renderer();
|
~renderer();
|
||||||
|
|
||||||
xcb_window_t window() const;
|
xcb_window_t window() const;
|
||||||
@ -95,6 +96,7 @@ class renderer
|
|||||||
const config& m_conf;
|
const config& m_conf;
|
||||||
const logger& m_log;
|
const logger& m_log;
|
||||||
const bar_settings& m_bar;
|
const bar_settings& m_bar;
|
||||||
|
background_manager& m_background;
|
||||||
|
|
||||||
int m_depth{32};
|
int m_depth{32};
|
||||||
xcb_window_t m_window;
|
xcb_window_t m_window;
|
||||||
|
@ -120,6 +120,9 @@ namespace signals {
|
|||||||
struct request_snapshot : public detail::value_signal<request_snapshot, string> {
|
struct request_snapshot : public detail::value_signal<request_snapshot, string> {
|
||||||
using base_type::base_type;
|
using base_type::base_type;
|
||||||
};
|
};
|
||||||
|
struct update_background : public detail::base_signal<update_background> {
|
||||||
|
using base_type::base_type;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ui_tray {
|
namespace ui_tray {
|
||||||
|
@ -38,6 +38,7 @@ namespace signals {
|
|||||||
struct shade_window;
|
struct shade_window;
|
||||||
struct unshade_window;
|
struct unshade_window;
|
||||||
struct request_snapshot;
|
struct request_snapshot;
|
||||||
|
struct update_background;
|
||||||
}
|
}
|
||||||
namespace ui_tray {
|
namespace ui_tray {
|
||||||
struct mapped_clients;
|
struct mapped_clients;
|
||||||
|
52
include/x11/background_manager.hpp
Normal file
52
include/x11/background_manager.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "events/signal_fwd.hpp"
|
||||||
|
#include "x11/extensions/fwd.hpp"
|
||||||
|
#include "x11/types.hpp"
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
class logger;
|
||||||
|
|
||||||
|
namespace cairo {
|
||||||
|
class surface;
|
||||||
|
class xcb_surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
class background_manager : public xpp::event::sink<evt::property_notify> {
|
||||||
|
public:
|
||||||
|
using make_type = background_manager&;
|
||||||
|
static make_type make();
|
||||||
|
|
||||||
|
explicit background_manager(connection& conn, signal_emitter& sig, const logger& log);
|
||||||
|
~background_manager();
|
||||||
|
|
||||||
|
void activate(xcb_window_t window, xcb_rectangle_t rect);
|
||||||
|
void deactivate();
|
||||||
|
|
||||||
|
cairo::surface* get_surface() const;
|
||||||
|
|
||||||
|
void handle(const evt::property_notify& evt);
|
||||||
|
private:
|
||||||
|
connection& m_connection;
|
||||||
|
signal_emitter& m_sig;
|
||||||
|
const logger& m_log;
|
||||||
|
xcb_window_t m_window;
|
||||||
|
|
||||||
|
xcb_rectangle_t m_rect{0, 0, 0U, 0U};
|
||||||
|
|
||||||
|
xcb_visualtype_t* m_visual = nullptr;
|
||||||
|
xcb_gcontext_t m_gcontext = XCB_NONE;
|
||||||
|
xcb_pixmap_t m_pixmap = XCB_NONE;
|
||||||
|
unique_ptr<cairo::xcb_surface> m_surface;
|
||||||
|
|
||||||
|
bool m_attached = false;
|
||||||
|
|
||||||
|
void allocate_resources();
|
||||||
|
void free_resources();
|
||||||
|
void fetch_root_pixmap();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
@ -126,6 +126,7 @@ class connection : public detail::connection_base<connection&, XPP_EXTENSION_LIS
|
|||||||
unsigned int event_mask = 0xFFFFFF, bool propagate = false) const;
|
unsigned int event_mask = 0xFFFFFF, bool propagate = false) const;
|
||||||
|
|
||||||
xcb_visualtype_t* visual_type(xcb_screen_t* screen, int match_depth = 32);
|
xcb_visualtype_t* visual_type(xcb_screen_t* screen, int match_depth = 32);
|
||||||
|
xcb_visualtype_t* visual_type_for_id(xcb_screen_t* screen, xcb_visualid_t visual_id);
|
||||||
|
|
||||||
bool root_pixmap(xcb_pixmap_t* pixmap, int* depth, xcb_rectangle_t* rect);
|
bool root_pixmap(xcb_pixmap_t* pixmap, int* depth, xcb_rectangle_t* rect);
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "cairo/context.hpp"
|
||||||
|
#include "cairo/surface.hpp"
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "components/logger.hpp"
|
#include "components/logger.hpp"
|
||||||
#include "components/types.hpp"
|
#include "components/types.hpp"
|
||||||
@ -32,6 +34,7 @@ using namespace std::chrono_literals;
|
|||||||
// fwd declarations
|
// fwd declarations
|
||||||
class connection;
|
class connection;
|
||||||
struct xembed_data;
|
struct xembed_data;
|
||||||
|
class background_manager;
|
||||||
|
|
||||||
struct tray_settings {
|
struct tray_settings {
|
||||||
tray_settings() = default;
|
tray_settings() = default;
|
||||||
@ -61,12 +64,12 @@ class tray_manager
|
|||||||
: public xpp::event::sink<evt::expose, evt::visibility_notify, evt::client_message, evt::configure_request,
|
: public xpp::event::sink<evt::expose, evt::visibility_notify, evt::client_message, evt::configure_request,
|
||||||
evt::resize_request, evt::selection_clear, evt::property_notify, evt::reparent_notify, evt::destroy_notify,
|
evt::resize_request, evt::selection_clear, evt::property_notify, evt::reparent_notify, evt::destroy_notify,
|
||||||
evt::map_notify, evt::unmap_notify>,
|
evt::map_notify, evt::unmap_notify>,
|
||||||
public signal_receiver<SIGN_PRIORITY_TRAY, signals::ui::visibility_change, signals::ui::dim_window> {
|
public signal_receiver<SIGN_PRIORITY_TRAY, signals::ui::visibility_change, signals::ui::dim_window, signals::ui::update_background> {
|
||||||
public:
|
public:
|
||||||
using make_type = unique_ptr<tray_manager>;
|
using make_type = unique_ptr<tray_manager>;
|
||||||
static make_type make();
|
static make_type make();
|
||||||
|
|
||||||
explicit tray_manager(connection& conn, signal_emitter& emitter, const logger& logger);
|
explicit tray_manager(connection& conn, signal_emitter& emitter, const logger& logger, background_manager& back);
|
||||||
|
|
||||||
~tray_manager();
|
~tray_manager();
|
||||||
|
|
||||||
@ -127,21 +130,21 @@ class tray_manager
|
|||||||
|
|
||||||
bool on(const signals::ui::visibility_change& evt);
|
bool on(const signals::ui::visibility_change& evt);
|
||||||
bool on(const signals::ui::dim_window& evt);
|
bool on(const signals::ui::dim_window& evt);
|
||||||
|
bool on(const signals::ui::update_background& evt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
connection& m_connection;
|
connection& m_connection;
|
||||||
signal_emitter& m_sig;
|
signal_emitter& m_sig;
|
||||||
const logger& m_log;
|
const logger& m_log;
|
||||||
|
background_manager& m_background;
|
||||||
vector<shared_ptr<tray_client>> m_clients;
|
vector<shared_ptr<tray_client>> m_clients;
|
||||||
|
|
||||||
tray_settings m_opts{};
|
tray_settings m_opts{};
|
||||||
|
|
||||||
xcb_gcontext_t m_gc{0};
|
xcb_gcontext_t m_gc{0};
|
||||||
xcb_pixmap_t m_pixmap{0};
|
xcb_pixmap_t m_pixmap{0};
|
||||||
|
unique_ptr<cairo::surface> m_surface;
|
||||||
xcb_pixmap_t m_rootpixmap{0};
|
unique_ptr<cairo::context> m_context;
|
||||||
int m_rootpixmap_depth{0};
|
|
||||||
xcb_rectangle_t m_rootpixmap_geom{0, 0, 0U, 0U};
|
|
||||||
|
|
||||||
unsigned int m_prevwidth{0U};
|
unsigned int m_prevwidth{0U};
|
||||||
unsigned int m_prevheight{0U};
|
unsigned int m_prevheight{0U};
|
||||||
|
@ -686,4 +686,10 @@ bool controller::on(const signals::ipc::hook& evt) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool controller::on(const signals::ui::update_background&) {
|
||||||
|
enqueue(make_update_evt(true));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
POLYBAR_NS_END
|
POLYBAR_NS_END
|
||||||
|
@ -77,7 +77,7 @@ void parser::codeblock(string&& data, const bar_settings& bar) {
|
|||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case 'B':
|
case 'B':
|
||||||
m_sig.emit(change_background{parse_color(value, bar.background)});
|
m_sig.emit(change_background{parse_color(value, 0x0)});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'F':
|
case 'F':
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
#include "utils/math.hpp"
|
#include "utils/math.hpp"
|
||||||
#include "x11/atoms.hpp"
|
#include "x11/atoms.hpp"
|
||||||
|
#include "x11/background_manager.hpp"
|
||||||
#include "x11/connection.hpp"
|
#include "x11/connection.hpp"
|
||||||
#include "x11/extensions/all.hpp"
|
#include "x11/extensions/all.hpp"
|
||||||
#include "x11/winspec.hpp"
|
#include "x11/winspec.hpp"
|
||||||
@ -25,7 +26,8 @@ renderer::make_type renderer::make(const bar_settings& bar) {
|
|||||||
signal_emitter::make(),
|
signal_emitter::make(),
|
||||||
config::make(),
|
config::make(),
|
||||||
logger::make(),
|
logger::make(),
|
||||||
forward<decltype(bar)>(bar));
|
forward<decltype(bar)>(bar),
|
||||||
|
background_manager::make());
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,13 +35,15 @@ renderer::make_type renderer::make(const bar_settings& bar) {
|
|||||||
* Construct renderer instance
|
* Construct renderer instance
|
||||||
*/
|
*/
|
||||||
renderer::renderer(
|
renderer::renderer(
|
||||||
connection& conn, signal_emitter& sig, const config& conf, const logger& logger, const bar_settings& bar)
|
connection& conn, signal_emitter& sig, const config& conf, const logger& logger, const bar_settings& bar, background_manager& background)
|
||||||
: m_connection(conn)
|
: m_connection(conn)
|
||||||
, m_sig(sig)
|
, m_sig(sig)
|
||||||
, m_conf(conf)
|
, m_conf(conf)
|
||||||
, m_log(logger)
|
, m_log(logger)
|
||||||
, m_bar(forward<const bar_settings&>(bar))
|
, m_bar(forward<const bar_settings&>(bar))
|
||||||
|
, m_background(background)
|
||||||
, m_rect(m_bar.inner_area()) {
|
, m_rect(m_bar.inner_area()) {
|
||||||
|
|
||||||
m_sig.attach(this);
|
m_sig.attach(this);
|
||||||
m_log.trace("renderer: Get TrueColor visual");
|
m_log.trace("renderer: Get TrueColor visual");
|
||||||
{
|
{
|
||||||
@ -160,6 +164,9 @@ renderer::renderer(
|
|||||||
m_log.info("Loaded font \"%s\" (name=%s, offset=%i, file=%s)", pattern, font->name(), offset, font->file());
|
m_log.info("Loaded font \"%s\" (name=%s, offset=%i, file=%s)", pattern, font->name(), offset, font->file());
|
||||||
*m_context << move(font);
|
*m_context << move(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_log.trace("Activate root background manager");
|
||||||
|
m_background.activate(m_window, m_bar.inner_area(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_comp_bg = m_conf.get<cairo_operator_t>("settings", "compositing-background", m_comp_bg);
|
m_comp_bg = m_conf.get<cairo_operator_t>("settings", "compositing-background", m_comp_bg);
|
||||||
@ -205,7 +212,7 @@ void renderer::begin(xcb_rectangle_t rect) {
|
|||||||
m_align = alignment::NONE;
|
m_align = alignment::NONE;
|
||||||
|
|
||||||
// Reset colors
|
// Reset colors
|
||||||
m_bg = m_bar.background;
|
m_bg = 0x0;
|
||||||
m_fg = m_bar.foreground;
|
m_fg = m_bar.foreground;
|
||||||
m_ul = m_bar.underline.color;
|
m_ul = m_bar.underline.color;
|
||||||
m_ol = m_bar.overline.color;
|
m_ol = m_bar.overline.color;
|
||||||
@ -214,6 +221,11 @@ void renderer::begin(xcb_rectangle_t rect) {
|
|||||||
m_context->save();
|
m_context->save();
|
||||||
m_context->clear();
|
m_context->clear();
|
||||||
|
|
||||||
|
// Draw the background on the new layer to make up for
|
||||||
|
// the areas not covered by the alignment blocks
|
||||||
|
fill_background();
|
||||||
|
|
||||||
|
|
||||||
// Create corner mask
|
// Create corner mask
|
||||||
if (m_bar.radius && m_cornermask == nullptr) {
|
if (m_bar.radius && m_cornermask == nullptr) {
|
||||||
m_context->save();
|
m_context->save();
|
||||||
@ -261,10 +273,6 @@ void renderer::end() {
|
|||||||
// so that it can be masked with the corner pattern
|
// so that it can be masked with the corner pattern
|
||||||
m_context->push();
|
m_context->push();
|
||||||
|
|
||||||
// Draw the background on the new layer to make up for
|
|
||||||
// the areas not covered by the alignment blocks
|
|
||||||
fill_background();
|
|
||||||
|
|
||||||
for (auto&& b : m_blocks) {
|
for (auto&& b : m_blocks) {
|
||||||
flush(b.first);
|
flush(b.first);
|
||||||
}
|
}
|
||||||
@ -495,6 +503,14 @@ void renderer::fill_background() {
|
|||||||
m_context->save();
|
m_context->save();
|
||||||
*m_context << m_comp_bg;
|
*m_context << m_comp_bg;
|
||||||
|
|
||||||
|
auto root_bg = m_background.get_surface();
|
||||||
|
if(root_bg != nullptr) {
|
||||||
|
m_log.trace_x("renderer: root background");
|
||||||
|
*m_context << *root_bg;
|
||||||
|
m_context->paint();
|
||||||
|
*m_context << CAIRO_OPERATOR_OVER;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_bar.background_steps.empty()) {
|
if (!m_bar.background_steps.empty()) {
|
||||||
m_log.trace_x("renderer: gradient background (steps=%lu)", m_bar.background_steps.size());
|
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};
|
*m_context << cairo::linear_gradient{0.0, 0.0 + m_rect.y, 0.0, 0.0 + m_rect.height, m_bar.background_steps};
|
||||||
@ -718,8 +734,6 @@ bool renderer::on(const signals::parser::change_alignment& evt) {
|
|||||||
m_blocks[m_align].y = 0.0;
|
m_blocks[m_align].y = 0.0;
|
||||||
m_context->push();
|
m_context->push();
|
||||||
m_log.trace_x("renderer: push(%i)", static_cast<int>(m_align));
|
m_log.trace_x("renderer: push(%i)", static_cast<int>(m_align));
|
||||||
|
|
||||||
fill_background();
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
143
src/x11/background_manager.cpp
Normal file
143
src/x11/background_manager.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#include "cairo/surface.hpp"
|
||||||
|
#include "events/signal.hpp"
|
||||||
|
#include "components/logger.hpp"
|
||||||
|
#include "x11/atoms.hpp"
|
||||||
|
#include "x11/connection.hpp"
|
||||||
|
#include "x11/background_manager.hpp"
|
||||||
|
#include "utils/factory.hpp"
|
||||||
|
#include "utils/math.hpp"
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
background_manager& background_manager::make() {
|
||||||
|
return *factory_util::singleton<background_manager>(connection::make(), signal_emitter::make(), logger::make());
|
||||||
|
}
|
||||||
|
|
||||||
|
background_manager::background_manager(
|
||||||
|
connection& conn, signal_emitter& sig, const logger& log)
|
||||||
|
: m_connection(conn)
|
||||||
|
, m_sig(sig)
|
||||||
|
, m_log(log) {
|
||||||
|
}
|
||||||
|
|
||||||
|
background_manager::~background_manager() {
|
||||||
|
free_resources();
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo::surface* background_manager::get_surface() const {
|
||||||
|
return m_surface.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void background_manager::activate(xcb_window_t window, xcb_rectangle_t rect) {
|
||||||
|
// ensure that we start from a clean state
|
||||||
|
//
|
||||||
|
// the size of the pixmap may need to be changed, etc.
|
||||||
|
// so the easiest way is to just re-allocate everything.
|
||||||
|
// it may be possible to be more clever here, but activate is
|
||||||
|
// not supposed to be called often so this shouldn't be a problem.
|
||||||
|
free_resources();
|
||||||
|
|
||||||
|
// make sure that we receive a notification when the background changes
|
||||||
|
if(!m_attached) {
|
||||||
|
m_connection.ensure_event_mask(m_connection.root(), XCB_EVENT_MASK_PROPERTY_CHANGE);
|
||||||
|
m_connection.flush();
|
||||||
|
m_connection.attach_sink(this, SINK_PRIORITY_SCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window = window;
|
||||||
|
m_rect = rect;
|
||||||
|
fetch_root_pixmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void background_manager::deactivate() {
|
||||||
|
free_resources();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void background_manager::allocate_resources() {
|
||||||
|
if(!m_visual) {
|
||||||
|
m_log.trace("background_manager: Finding root visual");
|
||||||
|
m_visual = m_connection.visual_type_for_id(m_connection.screen(), m_connection.screen()->root_visual);
|
||||||
|
m_log.trace("background_manager: Got root visual with depth %d", m_connection.screen()->root_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_pixmap == XCB_NONE) {
|
||||||
|
m_log.trace("background_manager: Allocating pixmap");
|
||||||
|
m_pixmap = m_connection.generate_id();
|
||||||
|
m_connection.create_pixmap(m_connection.screen()->root_depth, m_pixmap, m_window, m_rect.width, m_rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_gcontext == XCB_NONE) {
|
||||||
|
m_log.trace("background_manager: Allocating graphics context");
|
||||||
|
unsigned int mask = XCB_GC_GRAPHICS_EXPOSURES;
|
||||||
|
unsigned int value_list[1] = {0};
|
||||||
|
m_gcontext = m_connection.generate_id();
|
||||||
|
m_connection.create_gc(m_gcontext, m_pixmap, mask, value_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_surface) {
|
||||||
|
m_log.trace("background_manager: Allocating cairo surface");
|
||||||
|
m_surface = make_unique<cairo::xcb_surface>(m_connection, m_pixmap, m_visual, m_rect.width, m_rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_attached) {
|
||||||
|
m_connection.detach_sink(this, SINK_PRIORITY_SCREEN);
|
||||||
|
m_attached = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void background_manager::free_resources() {
|
||||||
|
m_surface.release();
|
||||||
|
m_visual = nullptr;
|
||||||
|
|
||||||
|
if(m_pixmap != XCB_NONE) {
|
||||||
|
m_connection.free_pixmap(m_pixmap);
|
||||||
|
m_pixmap = XCB_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_gcontext != XCB_NONE) {
|
||||||
|
m_connection.free_gc(m_gcontext);
|
||||||
|
m_gcontext = XCB_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void background_manager::fetch_root_pixmap() {
|
||||||
|
allocate_resources();
|
||||||
|
m_log.trace("background_manager: Fetching pixmap");
|
||||||
|
|
||||||
|
int pixmap_depth;
|
||||||
|
xcb_pixmap_t pixmap;
|
||||||
|
xcb_rectangle_t pixmap_geom;
|
||||||
|
|
||||||
|
if (!m_connection.root_pixmap(&pixmap, &pixmap_depth, &pixmap_geom)) {
|
||||||
|
free_resources();
|
||||||
|
return m_log.err("background_manager: Failed to get root pixmap for background (realloc=%i)", realloc);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto src_x = math_util::cap(m_rect.x, pixmap_geom.x, int16_t(pixmap_geom.x + pixmap_geom.width));
|
||||||
|
auto src_y = math_util::cap(m_rect.y, pixmap_geom.y, int16_t(pixmap_geom.y + pixmap_geom.height));
|
||||||
|
auto h = math_util::min(m_rect.height, pixmap_geom.height);
|
||||||
|
auto w = math_util::min(m_rect.width, pixmap_geom.width);
|
||||||
|
|
||||||
|
m_log.trace("background_manager: Copying from root pixmap (%d) %dx%d+%dx%d", pixmap, w, h, src_x, src_y);
|
||||||
|
try {
|
||||||
|
m_connection.copy_area_checked(pixmap, m_pixmap, m_gcontext, src_x, src_y, 0, 0, w, h);
|
||||||
|
} catch (const exception& err) {
|
||||||
|
m_log.err("background_manager: Failed to copy slice of root pixmap (%s)", err.what());
|
||||||
|
free_resources();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void background_manager::handle(const evt::property_notify& evt) {
|
||||||
|
// if region that we should observe is empty, don't do anything
|
||||||
|
if(m_rect.width == 0 || m_rect.height == 0) return;
|
||||||
|
|
||||||
|
if (evt->atom == _XROOTMAP_ID || evt->atom == _XSETROOT_ID || evt->atom == ESETROOT_PMAP_ID) {
|
||||||
|
fetch_root_pixmap();
|
||||||
|
m_sig.emit(signals::ui::update_background());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
@ -165,6 +165,19 @@ xcb_visualtype_t* connection::visual_type(xcb_screen_t* screen, int match_depth)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
xcb_visualtype_t* connection::visual_type_for_id(xcb_screen_t* screen, xcb_visualid_t visual_id) {
|
||||||
|
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(screen);
|
||||||
|
if (depth_iter.data) {
|
||||||
|
for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
|
||||||
|
for (auto it = xcb_depth_visuals_iterator(depth_iter.data); it.rem; xcb_visualtype_next(&it)) {
|
||||||
|
if(it.data->visual_id == visual_id) return it.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query root window pixmap
|
* Query root window pixmap
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include <xcb/xcb_image.h>
|
#include <xcb/xcb_image.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include "cairo/context.hpp"
|
||||||
|
#include "cairo/surface.hpp"
|
||||||
#include "components/config.hpp"
|
#include "components/config.hpp"
|
||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
#include "events/signal.hpp"
|
#include "events/signal.hpp"
|
||||||
@ -9,6 +11,7 @@
|
|||||||
#include "utils/math.hpp"
|
#include "utils/math.hpp"
|
||||||
#include "utils/memory.hpp"
|
#include "utils/memory.hpp"
|
||||||
#include "utils/process.hpp"
|
#include "utils/process.hpp"
|
||||||
|
#include "x11/background_manager.hpp"
|
||||||
#include "x11/ewmh.hpp"
|
#include "x11/ewmh.hpp"
|
||||||
#include "x11/icccm.hpp"
|
#include "x11/icccm.hpp"
|
||||||
#include "x11/tray_manager.hpp"
|
#include "x11/tray_manager.hpp"
|
||||||
@ -37,11 +40,11 @@ POLYBAR_NS
|
|||||||
* Create instance
|
* Create instance
|
||||||
*/
|
*/
|
||||||
tray_manager::make_type tray_manager::make() {
|
tray_manager::make_type tray_manager::make() {
|
||||||
return factory_util::unique<tray_manager>(connection::make(), signal_emitter::make(), logger::make());
|
return factory_util::unique<tray_manager>(connection::make(), signal_emitter::make(), logger::make(), background_manager::make());
|
||||||
}
|
}
|
||||||
|
|
||||||
tray_manager::tray_manager(connection& conn, signal_emitter& emitter, const logger& logger)
|
tray_manager::tray_manager(connection& conn, signal_emitter& emitter, const logger& logger, background_manager& back)
|
||||||
: m_connection(conn), m_sig(emitter), m_log(logger) {
|
: m_connection(conn), m_sig(emitter), m_log(logger), m_background(back) {
|
||||||
m_connection.attach_sink(this, SINK_PRIORITY_TRAY);
|
m_connection.attach_sink(this, SINK_PRIORITY_TRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,9 +134,9 @@ void tray_manager::setup(const bar_settings& bar_opts) {
|
|||||||
m_opts.background = bar_opts.background;
|
m_opts.background = bar_opts.background;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color_util::alpha_channel(m_opts.background) == 0) {
|
if (color_util::alpha_channel(m_opts.background) != 1) {
|
||||||
m_opts.transparent = true;
|
m_opts.transparent = true;
|
||||||
m_opts.background = 0;
|
//m_opts.background = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +260,8 @@ void tray_manager::deactivate(bool clear_selection) {
|
|||||||
m_log.trace("tray: Destroy window");
|
m_log.trace("tray: Destroy window");
|
||||||
m_connection.destroy_window(m_tray);
|
m_connection.destroy_window(m_tray);
|
||||||
}
|
}
|
||||||
|
m_context.release();
|
||||||
|
m_surface.release();
|
||||||
if (m_pixmap) {
|
if (m_pixmap) {
|
||||||
m_connection.free_pixmap(m_pixmap);
|
m_connection.free_pixmap(m_pixmap);
|
||||||
}
|
}
|
||||||
@ -267,7 +272,6 @@ void tray_manager::deactivate(bool clear_selection) {
|
|||||||
m_tray = 0;
|
m_tray = 0;
|
||||||
m_pixmap = 0;
|
m_pixmap = 0;
|
||||||
m_gc = 0;
|
m_gc = 0;
|
||||||
m_rootpixmap = 0;
|
|
||||||
m_prevwidth = 0;
|
m_prevwidth = 0;
|
||||||
m_prevheight = 0;
|
m_prevheight = 0;
|
||||||
m_opts.configured_x = 0;
|
m_opts.configured_x = 0;
|
||||||
@ -384,75 +388,30 @@ void tray_manager::reconfigure_clients() {
|
|||||||
void tray_manager::reconfigure_bg(bool realloc) {
|
void tray_manager::reconfigure_bg(bool realloc) {
|
||||||
if (!m_opts.transparent || m_clients.empty() || !m_mapped) {
|
if (!m_opts.transparent || m_clients.empty() || !m_mapped) {
|
||||||
return;
|
return;
|
||||||
} else if (!m_rootpixmap) {
|
};
|
||||||
realloc = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto w = calculate_w();
|
|
||||||
auto h = calculate_h();
|
|
||||||
|
|
||||||
if ((!w || (w == m_prevwidth && h == m_prevheight)) && !realloc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log.trace("tray: Reconfigure bg (realloc=%i)", realloc);
|
m_log.trace("tray: Reconfigure bg (realloc=%i)", realloc);
|
||||||
|
|
||||||
if (realloc && !m_connection.root_pixmap(&m_rootpixmap, &m_rootpixmap_depth, &m_rootpixmap_geom)) {
|
|
||||||
return m_log.err("Failed to get root pixmap for tray background (realloc=%i)", realloc);
|
|
||||||
} else if (realloc) {
|
|
||||||
// clang-format off
|
|
||||||
m_log.info("Tray root pixmap (rootpmap=%s, geom=%dx%d+%d+%d, tray=%s, pmap=%s, gc=%s)",
|
|
||||||
m_connection.id(m_rootpixmap),
|
|
||||||
m_rootpixmap_geom.width,
|
|
||||||
m_rootpixmap_geom.height,
|
|
||||||
m_rootpixmap_geom.x,
|
|
||||||
m_rootpixmap_geom.y,
|
|
||||||
m_connection.id(m_tray),
|
|
||||||
m_connection.id(m_pixmap),
|
|
||||||
m_connection.id(m_gc));
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
m_prevwidth = w;
|
|
||||||
m_prevheight = h;
|
|
||||||
|
|
||||||
|
auto w = calculate_w();
|
||||||
auto x = calculate_x(w);
|
auto x = calculate_x(w);
|
||||||
auto y = calculate_y();
|
auto y = calculate_y();
|
||||||
auto px = math_util::max(0, m_rootpixmap_geom.x + x);
|
|
||||||
auto py = math_util::max(0, m_rootpixmap_geom.y + y);
|
|
||||||
|
|
||||||
// Make sure we don't try to copy void content
|
if(!m_context) {
|
||||||
if (px + w > m_rootpixmap_geom.width) {
|
return m_log.err("tray: no context for drawing the background");
|
||||||
w -= px + w - m_rootpixmap_geom.width;
|
|
||||||
}
|
|
||||||
if (py + h > m_rootpixmap_geom.height) {
|
|
||||||
h -= py + h - m_rootpixmap_geom.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realloc) {
|
cairo::surface* surface = m_background.get_surface();
|
||||||
vector<unsigned char> image_data;
|
if(!surface) {
|
||||||
unsigned char image_depth;
|
return m_log.err("tray: no root surface");
|
||||||
|
|
||||||
try {
|
|
||||||
auto image_reply = m_connection.get_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_rootpixmap, px, py, w, h, XCB_COPY_PLANE);
|
|
||||||
image_depth = image_reply->depth;
|
|
||||||
std::back_insert_iterator<decltype(image_data)> back_it(image_data);
|
|
||||||
std::copy(image_reply.data().begin(), image_reply.data().end(), back_it);
|
|
||||||
} catch (const exception& err) {
|
|
||||||
m_log.err("Failed to get slice of root pixmap (%s)", err.what());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
m_connection.put_image_checked(
|
|
||||||
XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_gc, w, h, 0, 0, 0, image_depth, image_data.size(), image_data.data());
|
|
||||||
} catch (const exception& err) {
|
|
||||||
m_log.err("Failed to store slice of root pixmap (%s)", err.what());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_connection.copy_area_checked(m_rootpixmap, m_pixmap, m_gc, px, py, 0, 0, w, h);
|
m_context->clear();
|
||||||
|
*m_context << CAIRO_OPERATOR_SOURCE << *m_surface;
|
||||||
|
cairo_set_source_surface(*m_context, *surface, -x, -y);
|
||||||
|
m_context->paint();
|
||||||
|
*m_context << CAIRO_OPERATOR_OVER << m_opts.background;
|
||||||
|
m_context->paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -470,11 +429,13 @@ void tray_manager::refresh_window() {
|
|||||||
auto width = calculate_w();
|
auto width = calculate_w();
|
||||||
auto height = calculate_h();
|
auto height = calculate_h();
|
||||||
|
|
||||||
if (m_opts.transparent && !m_rootpixmap) {
|
if (m_opts.transparent && !m_context) {
|
||||||
xcb_rectangle_t rect{0, 0, static_cast<uint16_t>(width), static_cast<uint16_t>(height)};
|
xcb_rectangle_t rect{0, 0, static_cast<uint16_t>(width), static_cast<uint16_t>(height)};
|
||||||
m_connection.poly_fill_rectangle(m_pixmap, m_gc, 1, &rect);
|
m_connection.poly_fill_rectangle(m_pixmap, m_gc, 1, &rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(m_surface) m_surface->flush();
|
||||||
|
|
||||||
m_connection.clear_area(0, m_tray, 0, 0, width, height);
|
m_connection.clear_area(0, m_tray, 0, 0, width, height);
|
||||||
|
|
||||||
for (auto&& client : m_clients) {
|
for (auto&& client : m_clients) {
|
||||||
@ -546,7 +507,7 @@ void tray_manager::create_bg(bool realloc) {
|
|||||||
if (!m_opts.transparent) {
|
if (!m_opts.transparent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!realloc && m_pixmap && m_gc && m_rootpixmap) {
|
if (!realloc && m_pixmap && m_gc && m_surface && m_context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (realloc && m_pixmap) {
|
if (realloc && m_pixmap) {
|
||||||
@ -558,6 +519,13 @@ void tray_manager::create_bg(bool realloc) {
|
|||||||
m_gc = 0;
|
m_gc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(realloc && m_surface) {
|
||||||
|
m_surface.release();
|
||||||
|
}
|
||||||
|
if(realloc && m_context) {
|
||||||
|
m_context.release();
|
||||||
|
}
|
||||||
|
|
||||||
auto w = m_opts.width_max;
|
auto w = m_opts.width_max;
|
||||||
auto h = calculate_h();
|
auto h = calculate_h();
|
||||||
|
|
||||||
@ -584,6 +552,21 @@ void tray_manager::create_bg(bool realloc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!m_surface) {
|
||||||
|
xcb_visualtype_t* visual = m_connection.visual_type_for_id(m_connection.screen(), m_connection.screen()->root_visual);
|
||||||
|
if(!visual) {
|
||||||
|
return m_log.err("Failed to get root visual for tray background");
|
||||||
|
}
|
||||||
|
m_surface = make_unique<cairo::xcb_surface>(m_connection, m_pixmap, visual, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_context) {
|
||||||
|
m_context = make_unique<cairo::context>(*m_surface, m_log);
|
||||||
|
m_context->clear();
|
||||||
|
*m_context << CAIRO_OPERATOR_SOURCE << m_opts.background;
|
||||||
|
m_context->paint();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m_connection.change_window_attributes_checked(m_tray, XCB_CW_BACK_PIXMAP, &m_pixmap);
|
m_connection.change_window_attributes_checked(m_tray, XCB_CW_BACK_PIXMAP, &m_pixmap);
|
||||||
} catch (const exception& err) {
|
} catch (const exception& err) {
|
||||||
@ -1155,4 +1138,10 @@ bool tray_manager::on(const signals::ui::dim_window& evt) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool tray_manager::on(const signals::ui::update_background&) {
|
||||||
|
redraw_window(true);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
POLYBAR_NS_END
|
POLYBAR_NS_END
|
||||||
|
Loading…
Reference in New Issue
Block a user