Add pseudo transparency back to tray icons
This commit is contained in:
parent
def2682b8c
commit
463ef963a1
@ -82,6 +82,10 @@ enum class strut {
|
|||||||
struct position {
|
struct position {
|
||||||
int x{0};
|
int x{0};
|
||||||
int y{0};
|
int y{0};
|
||||||
|
|
||||||
|
bool operator==(const position& other) {
|
||||||
|
return this->x == other.x && this->y == other.y;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct size {
|
struct size {
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
#include "cairo/context.hpp"
|
||||||
|
#include "cairo/surface.hpp"
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "utils/concurrency.hpp"
|
#include "utils/concurrency.hpp"
|
||||||
|
#include "x11/background_manager.hpp"
|
||||||
#include "x11/xembed.hpp"
|
#include "x11/xembed.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -19,7 +22,8 @@ class connection;
|
|||||||
|
|
||||||
class tray_client : public non_copyable_mixin, public non_movable_mixin {
|
class tray_client : public non_copyable_mixin, public non_movable_mixin {
|
||||||
public:
|
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();
|
~tray_client();
|
||||||
|
|
||||||
string name() const;
|
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 add_to_save_set() const;
|
||||||
|
|
||||||
void ensure_state() const;
|
void ensure_state() const;
|
||||||
void reconfigure(int x, int y) const;
|
void set_position(int x, int y);
|
||||||
void configure_notify() const;
|
void configure_notify() const;
|
||||||
|
|
||||||
|
void update_bg() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void observe_background();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const logger& m_log;
|
const logger& m_log;
|
||||||
|
|
||||||
@ -107,6 +116,13 @@ class tray_client : public non_copyable_mixin, public non_movable_mixin {
|
|||||||
bool m_hidden{false};
|
bool m_hidden{false};
|
||||||
|
|
||||||
size m_size;
|
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
|
POLYBAR_NS_END
|
||||||
|
@ -86,8 +86,7 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::client_message, e
|
|||||||
protected:
|
protected:
|
||||||
void reconfigure_window();
|
void reconfigure_window();
|
||||||
void reconfigure_clients();
|
void reconfigure_clients();
|
||||||
void refresh_window();
|
void redraw_clients();
|
||||||
void redraw_window();
|
|
||||||
|
|
||||||
void query_atom();
|
void query_atom();
|
||||||
void set_tray_colors();
|
void set_tray_colors();
|
||||||
|
@ -19,8 +19,15 @@ POLYBAR_NS
|
|||||||
* 2. Use pseudo-transparency when activated (make sure the depths match)
|
* 2. Use pseudo-transparency when activated (make sure the depths match)
|
||||||
* 3. Use background color
|
* 3. Use background color
|
||||||
*/
|
*/
|
||||||
tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray, xcb_window_t win, size s)
|
tray_client::tray_client(
|
||||||
: m_log(log), m_connection(conn), m_name(ewmh_util::get_wm_name(win)), m_client(win), m_size(s) {
|
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 geom = conn.get_geometry(win);
|
||||||
auto attrs = conn.get_window_attributes(win);
|
auto attrs = conn.get_window_attributes(win);
|
||||||
int client_depth = geom->depth;
|
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
|
// clang-format off
|
||||||
m_wrapper = winspec(conn)
|
m_wrapper = winspec(conn)
|
||||||
<< cw_size(s.h, s.w)
|
<< cw_size(s.w, s.h)
|
||||||
<< cw_pos(0, 0)
|
<< cw_pos(0, 0)
|
||||||
<< cw_depth(client_depth)
|
<< cw_depth(client_depth)
|
||||||
<< cw_visual(client_visual)
|
<< cw_visual(client_visual)
|
||||||
<< cw_parent(tray)
|
<< cw_parent(parent)
|
||||||
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
|
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
|
||||||
// TODO add proper pixmap
|
// The X server requires the border pixel to be defined if the depth doesn't match the parent (bar) window
|
||||||
<< 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
|
|
||||||
<< cw_params_border_pixel(conn.screen()->black_pixel)
|
<< cw_params_border_pixel(conn.screen()->black_pixel)
|
||||||
<< cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED)
|
<< cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED)
|
||||||
<< cw_params_save_under(true)
|
<< 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_params_colormap(client_colormap)
|
||||||
<< cw_flush(true);
|
<< cw_flush(true);
|
||||||
// clang-format on
|
// 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<uint32_t, 32> 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<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() {
|
tray_client::~tray_client() {
|
||||||
@ -94,11 +142,6 @@ void tray_client::update_client_attributes() const {
|
|||||||
XCB_AUX_ADD_PARAM(
|
XCB_AUX_ADD_PARAM(
|
||||||
&configure_mask, &configure_params, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY);
|
&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);
|
connection::pack_values(configure_mask, &configure_params, configure_values);
|
||||||
|
|
||||||
m_log.trace("%s: Update client window", name());
|
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);
|
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;
|
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_params_configure_window_t configure_params{};
|
||||||
|
|
||||||
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, width, m_size.w);
|
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);
|
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, y, 0);
|
||||||
connection::pack_values(configure_mask, &configure_params, configure_values);
|
connection::pack_values(configure_mask, &configure_params, configure_values);
|
||||||
m_connection.configure_window_checked(client(), configure_mask, configure_values.data());
|
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*>(¬ify));
|
m_connection.send_event_checked(false, client(), mask, reinterpret_cast<const char*>(¬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<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
|
POLYBAR_NS_END
|
||||||
|
@ -132,7 +132,7 @@ void tray_manager::activate() {
|
|||||||
try {
|
try {
|
||||||
set_tray_colors();
|
set_tray_colors();
|
||||||
set_tray_orientation();
|
set_tray_orientation();
|
||||||
set_tray_visual();
|
// set_tray_visual();
|
||||||
} catch (const exception& err) {
|
} catch (const exception& err) {
|
||||||
m_log.err(err.what());
|
m_log.err(err.what());
|
||||||
m_log.err("Cannot activate tray manager... failed to setup window");
|
m_log.err("Cannot activate tray manager... failed to setup window");
|
||||||
@ -278,7 +278,7 @@ void tray_manager::reconfigure_clients() {
|
|||||||
client->ensure_state();
|
client->ensure_state();
|
||||||
|
|
||||||
if (client->mapped()) {
|
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;
|
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()) {
|
if (!is_visible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -307,7 +307,7 @@ void tray_manager::refresh_window() {
|
|||||||
for (auto& client : m_clients) {
|
for (auto& client : m_clients) {
|
||||||
try {
|
try {
|
||||||
if (client->mapped()) {
|
if (client->mapped()) {
|
||||||
client->clear_window();
|
client->update_bg();
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
m_log.err("tray: Failed to clear %s (%s)", client->name(), e.what());
|
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();
|
m_connection.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redraw window
|
|
||||||
*
|
|
||||||
* TODO better name
|
|
||||||
*/
|
|
||||||
void tray_manager::redraw_window() {
|
|
||||||
refresh_window();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the systray selection atom
|
* Find the systray selection atom
|
||||||
*/
|
*/
|
||||||
@ -371,6 +362,7 @@ void tray_manager::set_tray_orientation() {
|
|||||||
XCB_ATOM_CARDINAL, 32, 1, &orientation);
|
XCB_ATOM_CARDINAL, 32, 1, &orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove
|
||||||
void tray_manager::set_tray_visual() {
|
void tray_manager::set_tray_visual() {
|
||||||
// TODO use bar visual
|
// TODO use bar visual
|
||||||
const uint32_t visualid = m_connection.visual_type(XCB_VISUAL_CLASS_TRUE_COLOR, 32)->visual_id;
|
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));
|
m_log.info("tray: Processing docking request from '%s' (%s)", ewmh_util::get_wm_name(win), m_connection.id(win));
|
||||||
|
|
||||||
try {
|
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 {
|
try {
|
||||||
client->query_xembed();
|
client->query_xembed();
|
||||||
@ -578,7 +571,7 @@ bool tray_manager::change_visibility(bool visible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!m_hidden) {
|
if (!m_hidden) {
|
||||||
redraw_window();
|
redraw_clients();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_connection.flush();
|
m_connection.flush();
|
||||||
@ -591,7 +584,7 @@ bool tray_manager::change_visibility(bool visible) {
|
|||||||
*/
|
*/
|
||||||
void tray_manager::handle(const evt::expose& evt) {
|
void tray_manager::handle(const evt::expose& evt) {
|
||||||
if (is_active() && !m_clients.empty() && evt->count == 0) {
|
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)) {
|
} else if (is_active() && is_embedded(evt->window)) {
|
||||||
m_log.info("tray: Received destroy_notify for client, remove...");
|
m_log.info("tray: Received destroy_notify for client, remove...");
|
||||||
remove_client(evt->window);
|
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) {
|
void tray_manager::handle(const evt::map_notify& evt) {
|
||||||
if (is_active() && evt->window == m_opts.selection_owner) {
|
if (is_active() && evt->window == m_opts.selection_owner) {
|
||||||
m_log.trace("tray: Received map_notify for selection owner");
|
m_log.trace("tray: Received map_notify for selection owner");
|
||||||
redraw_window();
|
redraw_clients();
|
||||||
} else if (is_embedded(evt->window)) {
|
} else if (is_embedded(evt->window)) {
|
||||||
auto client = find_client(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&) {
|
bool tray_manager::on(const signals::ui::update_background&) {
|
||||||
redraw_window();
|
redraw_clients();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user