Add pseudo transparency back to tray icons

This commit is contained in:
patrick96 2022-10-01 22:24:23 +02:00
parent def2682b8c
commit 463ef963a1
No known key found for this signature in database
GPG Key ID: 521E5E03AEBCA1A7
5 changed files with 132 additions and 41 deletions

View File

@ -82,6 +82,10 @@ enum class strut {
struct position {
int x{0};
int y{0};
bool operator==(const position& other) {
return this->x == other.x && this->y == other.y;
}
};
struct size {

View File

@ -2,8 +2,11 @@
#include <xcb/xcb.h>
#include "cairo/context.hpp"
#include "cairo/surface.hpp"
#include "common.hpp"
#include "utils/concurrency.hpp"
#include "x11/background_manager.hpp"
#include "x11/xembed.hpp"
/*
@ -19,7 +22,8 @@ class connection;
class tray_client : public non_copyable_mixin, public non_movable_mixin {
public:
explicit tray_client(const logger& log, connection& conn, xcb_window_t tray, xcb_window_t win, size s);
explicit tray_client(
const logger& log, connection& conn, xcb_window_t parent, xcb_window_t win, size s, uint32_t desired_background);
~tray_client();
string name() const;
@ -51,9 +55,14 @@ class tray_client : public non_copyable_mixin, public non_movable_mixin {
void add_to_save_set() const;
void ensure_state() const;
void reconfigure(int x, int y) const;
void set_position(int x, int y);
void configure_notify() const;
void update_bg() const;
protected:
void observe_background();
protected:
const logger& m_log;
@ -107,6 +116,13 @@ class tray_client : public non_copyable_mixin, public non_movable_mixin {
bool m_hidden{false};
size m_size;
position m_pos{0, 0};
rgba m_desired_background;
background_manager& m_background_manager;
shared_ptr<bg_slice> m_bg_slice;
unique_ptr<cairo::context> m_context;
unique_ptr<cairo::xcb_surface> m_surface;
};
POLYBAR_NS_END

View File

@ -86,8 +86,7 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::client_message, e
protected:
void reconfigure_window();
void reconfigure_clients();
void refresh_window();
void redraw_window();
void redraw_clients();
void query_atom();
void set_tray_colors();

View File

@ -19,8 +19,15 @@ POLYBAR_NS
* 2. Use pseudo-transparency when activated (make sure the depths match)
* 3. Use background color
*/
tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray, xcb_window_t win, size s)
: m_log(log), m_connection(conn), m_name(ewmh_util::get_wm_name(win)), m_client(win), m_size(s) {
tray_client::tray_client(
const logger& log, connection& conn, xcb_window_t parent, xcb_window_t win, size s, uint32_t desired_background)
: m_log(log)
, m_connection(conn)
, m_name(ewmh_util::get_wm_name(win))
, m_client(win)
, m_size(s)
, m_desired_background(desired_background)
, m_background_manager(background_manager::make()) {
auto geom = conn.get_geometry(win);
auto attrs = conn.get_window_attributes(win);
int client_depth = geom->depth;
@ -37,16 +44,13 @@ tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray,
*/
// clang-format off
m_wrapper = winspec(conn)
<< cw_size(s.h, s.w)
<< cw_size(s.w, s.h)
<< cw_pos(0, 0)
<< cw_depth(client_depth)
<< cw_visual(client_visual)
<< cw_parent(tray)
<< cw_parent(parent)
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
// TODO add proper pixmap
<< cw_params_back_pixmap(XCB_PIXMAP_NONE)
// << cw_params_back_pixel(0x00ff00)
// The X server requires the border pixel to be defined if the depth doesn't match the parent window
// The X server requires the border pixel to be defined if the depth doesn't match the parent (bar) window
<< cw_params_border_pixel(conn.screen()->black_pixel)
<< cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED)
<< cw_params_save_under(true)
@ -57,6 +61,50 @@ tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray,
<< cw_params_colormap(client_colormap)
<< cw_flush(true);
// clang-format on
// TODO destroy in destructor
xcb_pixmap_t pixmap = m_connection.generate_id();
try {
m_connection.create_pixmap_checked(client_depth, pixmap, m_wrapper, s.w, s.h);
} catch (const std::exception& err) {
// TODO in case of error, fall back to desired_background
m_log.err("Failed to create pixmap for tray background (err: %s)", err.what());
throw;
}
try {
m_connection.change_window_attributes_checked(m_wrapper, XCB_CW_BACK_PIXMAP, &pixmap);
} catch (const std::exception& err) {
// TODO in case of error, fall back to desired_background
m_log.err("Failed to set tray window back pixmap (%s)", err.what());
throw;
}
// TODO destroy in destructor
xcb_gcontext_t gc = m_connection.generate_id();
try {
xcb_params_gc_t params{};
uint32_t mask = 0;
XCB_AUX_ADD_PARAM(&mask, &params, graphics_exposures, 1);
std::array<uint32_t, 32> values;
connection::pack_values(mask, &params, values);
m_connection.create_gc_checked(gc, pixmap, mask, values.data());
} catch (const std::exception& err) {
m_log.err("Failed to create gcontext for tray background (err: %s)", err.what());
throw;
}
xcb_visualtype_t* visual = m_connection.visual_type_for_id(client_visual);
if (!visual) {
// TODO in case of error, fall back to desired_background
throw std::runtime_error("Failed to get root visual for tray background");
}
m_surface = make_unique<cairo::xcb_surface>(m_connection, pixmap, visual, s.w, s.h);
m_context = make_unique<cairo::context>(*m_surface, m_log);
observe_background();
}
tray_client::~tray_client() {
@ -94,11 +142,6 @@ void tray_client::update_client_attributes() const {
XCB_AUX_ADD_PARAM(
&configure_mask, &configure_params, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY);
// TODO
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, back_pixel, 0xff0000ff);
// TODO
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, back_pixmap, XCB_PIXMAP_NONE);
connection::pack_values(configure_mask, &configure_params, configure_values);
m_log.trace("%s: Update client window", name());
@ -220,13 +263,21 @@ void tray_client::ensure_state() const {
}
/**
* Configure window size
* Configure window position
*/
void tray_client::reconfigure(int x, int y) const {
void tray_client::set_position(int x, int y) {
m_log.trace("%s: moving to (%d, %d)", name(), x, y);
position new_pos{x, y};
if (new_pos == m_pos) {
return;
}
m_pos = new_pos;
uint32_t configure_mask = 0;
std::array<uint32_t, 32> configure_values{};
array<uint32_t, 32> configure_values{};
xcb_params_configure_window_t configure_params{};
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, width, m_size.w);
@ -243,6 +294,9 @@ void tray_client::reconfigure(int x, int y) const {
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, y, 0);
connection::pack_values(configure_mask, &configure_params, configure_values);
m_connection.configure_window_checked(client(), configure_mask, configure_values.data());
// The position has changed, we need a new background slice.
observe_background();
}
/**
@ -265,4 +319,30 @@ void tray_client::configure_notify() const {
m_connection.send_event_checked(false, client(), mask, reinterpret_cast<const char*>(&notify));
}
/**
* Redraw background using the observed background slice.
*/
void tray_client::update_bg() const {
m_log.trace("%s: Update background", name());
// Composite background slice with background color.
m_context->clear();
*m_context << CAIRO_OPERATOR_SOURCE << *m_bg_slice->get_surface();
m_context->paint();
*m_context << CAIRO_OPERATOR_OVER << m_desired_background;
m_context->paint();
m_surface->flush();
clear_window();
m_connection.flush();
}
void tray_client::observe_background() {
xcb_rectangle_t rect{0, 0, static_cast<uint16_t>(m_size.w), static_cast<uint16_t>(m_size.h)};
m_bg_slice = m_background_manager.observe(rect, m_wrapper);
update_bg();
}
POLYBAR_NS_END

View File

@ -132,7 +132,7 @@ void tray_manager::activate() {
try {
set_tray_colors();
set_tray_orientation();
set_tray_visual();
// set_tray_visual();
} catch (const exception& err) {
m_log.err(err.what());
m_log.err("Cannot activate tray manager... failed to setup window");
@ -278,7 +278,7 @@ void tray_manager::reconfigure_clients() {
client->ensure_state();
if (client->mapped()) {
client->reconfigure(x, calculate_client_y());
client->set_position(x, calculate_client_y());
}
x += m_opts.client_size.w + m_opts.spacing;
@ -295,9 +295,9 @@ void tray_manager::reconfigure_clients() {
}
/**
* Refresh the bar window by clearing it along with each client window
* Redraw client windows.
*/
void tray_manager::refresh_window() {
void tray_manager::redraw_clients() {
if (!is_visible()) {
return;
}
@ -307,7 +307,7 @@ void tray_manager::refresh_window() {
for (auto& client : m_clients) {
try {
if (client->mapped()) {
client->clear_window();
client->update_bg();
}
} catch (const std::exception& e) {
m_log.err("tray: Failed to clear %s (%s)", client->name(), e.what());
@ -317,15 +317,6 @@ void tray_manager::refresh_window() {
m_connection.flush();
}
/**
* Redraw window
*
* TODO better name
*/
void tray_manager::redraw_window() {
refresh_window();
}
/**
* Find the systray selection atom
*/
@ -371,6 +362,7 @@ void tray_manager::set_tray_orientation() {
XCB_ATOM_CARDINAL, 32, 1, &orientation);
}
// TODO remove
void tray_manager::set_tray_visual() {
// TODO use bar visual
const uint32_t visualid = m_connection.visual_type(XCB_VISUAL_CLASS_TRUE_COLOR, 32)->visual_id;
@ -452,7 +444,8 @@ void tray_manager::process_docking_request(xcb_window_t win) {
m_log.info("tray: Processing docking request from '%s' (%s)", ewmh_util::get_wm_name(win), m_connection.id(win));
try {
auto client = make_unique<tray_client>(m_log, m_connection, m_opts.selection_owner, win, m_opts.client_size);
auto client = make_unique<tray_client>(
m_log, m_connection, m_opts.selection_owner, win, m_opts.client_size, m_bar_opts.background.value());
try {
client->query_xembed();
@ -578,7 +571,7 @@ bool tray_manager::change_visibility(bool visible) {
}
if (!m_hidden) {
redraw_window();
redraw_clients();
}
m_connection.flush();
@ -591,7 +584,7 @@ bool tray_manager::change_visibility(bool visible) {
*/
void tray_manager::handle(const evt::expose& evt) {
if (is_active() && !m_clients.empty() && evt->count == 0) {
redraw_window();
redraw_clients();
}
}
@ -740,7 +733,7 @@ void tray_manager::handle(const evt::destroy_notify& evt) {
} else if (is_active() && is_embedded(evt->window)) {
m_log.info("tray: Received destroy_notify for client, remove...");
remove_client(evt->window);
redraw_window();
redraw_clients();
}
}
@ -750,7 +743,7 @@ void tray_manager::handle(const evt::destroy_notify& evt) {
void tray_manager::handle(const evt::map_notify& evt) {
if (is_active() && evt->window == m_opts.selection_owner) {
m_log.trace("tray: Received map_notify for selection owner");
redraw_window();
redraw_clients();
} else if (is_embedded(evt->window)) {
auto client = find_client(evt->window);
@ -789,9 +782,8 @@ void tray_manager::handle(const evt::unmap_notify& evt) {
}
}
// TODO maybe remove signal
bool tray_manager::on(const signals::ui::update_background&) {
redraw_window();
redraw_clients();
return false;
}