From 463ef963a18560e876428ef7d75866976ec159b8 Mon Sep 17 00:00:00 2001
From: patrick96
Date: Sat, 1 Oct 2022 22:24:23 +0200
Subject: [PATCH] Add pseudo transparency back to tray icons
---
include/components/types.hpp | 4 ++
include/x11/tray_client.hpp | 20 ++++++-
include/x11/tray_manager.hpp | 3 +-
src/x11/tray_client.cpp | 112 ++++++++++++++++++++++++++++++-----
src/x11/tray_manager.cpp | 34 ++++-------
5 files changed, 132 insertions(+), 41 deletions(-)
diff --git a/include/components/types.hpp b/include/components/types.hpp
index 9c152654..3f8b9831 100644
--- a/include/components/types.hpp
+++ b/include/components/types.hpp
@@ -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 {
diff --git a/include/x11/tray_client.hpp b/include/x11/tray_client.hpp
index eb1a9189..0edd37a1 100644
--- a/include/x11/tray_client.hpp
+++ b/include/x11/tray_client.hpp
@@ -2,8 +2,11 @@
#include
+#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 m_bg_slice;
+ unique_ptr m_context;
+ unique_ptr m_surface;
};
POLYBAR_NS_END
diff --git a/include/x11/tray_manager.hpp b/include/x11/tray_manager.hpp
index 4b27fd41..be453da4 100644
--- a/include/x11/tray_manager.hpp
+++ b/include/x11/tray_manager.hpp
@@ -86,8 +86,7 @@ class tray_manager : public xpp::event::sinkdepth;
@@ -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, ¶ms, graphics_exposures, 1);
+ std::array values;
+ connection::pack_values(mask, ¶ms, 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(m_connection, pixmap, visual, s.w, s.h);
+ m_context = make_unique(*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 configure_values{};
+ array 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(¬ify));
}
+/**
+ * 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(m_size.w), static_cast(m_size.h)};
+ m_bg_slice = m_background_manager.observe(rect, m_wrapper);
+
+ update_bg();
+}
+
POLYBAR_NS_END
diff --git a/src/x11/tray_manager.cpp b/src/x11/tray_manager.cpp
index 0c7f94e6..7d1f5b3d 100644
--- a/src/x11/tray_manager.cpp
+++ b/src/x11/tray_manager.cpp
@@ -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(m_log, m_connection, m_opts.selection_owner, win, m_opts.client_size);
+ auto client = make_unique(
+ 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;
}