fix(tray): Improve pseudo-transparency handling

- Make sure previously drawn content is cleared
- Redraw tray clients on XCB_EXPOSE
- Prevent void parts of the root pixmap being copied

Ref #187
This commit is contained in:
Michael Carlberg 2016-12-05 03:49:57 +01:00
parent 4c50853044
commit 7f5117b7cc
5 changed files with 123 additions and 13 deletions

View File

@ -57,6 +57,12 @@ class renderer {
void debug_hints(); void debug_hints();
#endif #endif
protected:
struct reserve_area {
edge side{edge::NONE};
uint16_t size{0U};
};
private: private:
connection& m_connection; connection& m_connection;
const logger& m_log; const logger& m_log;
@ -66,6 +72,9 @@ class renderer {
xcb_rectangle_t m_rect{0, 0, 0U, 0U}; xcb_rectangle_t m_rect{0, 0, 0U, 0U};
xcb_rectangle_t m_cleared{0, 0, 0U, 0U};
reserve_area m_cleararea{};
xcb_window_t m_window; xcb_window_t m_window;
xcb_colormap_t m_colormap; xcb_colormap_t m_colormap;
xcb_visualtype_t* m_visual; xcb_visualtype_t* m_visual;

24
include/x11/generic.hpp Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <xcb/xcb.h>
#include "common.hpp"
POLYBAR_NS
namespace {
inline bool operator==(const xcb_rectangle_t& a, const xcb_rectangle_t& b) {
return a.width == b.width && a.height == b.height && a.x == b.x && a.y == b.y;
}
inline bool operator!=(const xcb_rectangle_t& a, const xcb_rectangle_t& b) {
return !(a == b);
}
inline bool operator==(const xcb_rectangle_t& a, int b) {
return a.width == b && a.height == b && a.x == b && a.y == b;
}
inline bool operator!=(const xcb_rectangle_t& a, int b) {
return !(a == b);
}
}
POLYBAR_NS_END

View File

@ -68,6 +68,10 @@ class tray_client {
~tray_client(); ~tray_client();
uint16_t width() const;
uint16_t height() const;
void clear_window() const;
bool match(const xcb_window_t& win) const; bool match(const xcb_window_t& win) const;
bool mapped() const; bool mapped() const;
void mapped(bool state); void mapped(bool state);
@ -114,7 +118,7 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
void reconfigure_clients(); void reconfigure_clients();
void reconfigure_bg(bool realloc = false); void reconfigure_bg(bool realloc = false);
void refresh_window(); void refresh_window();
void redraw_window(); void redraw_window(bool realloc_bg = false);
void query_atom(); void query_atom();
void create_window(); void create_window();

View File

@ -1,9 +1,11 @@
#include "components/renderer.hpp" #include "components/renderer.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "components/types.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/draw.hpp" #include "x11/draw.hpp"
#include "x11/fonts.hpp" #include "x11/fonts.hpp"
#include "x11/generic.hpp"
#include "x11/winspec.hpp" #include "x11/winspec.hpp"
#include "x11/xlib.hpp" #include "x11/xlib.hpp"
#include "x11/xutils.hpp" #include "x11/xutils.hpp"
@ -206,7 +208,39 @@ void renderer::flush(bool clear) {
right.width += m_bar.borders.at(edge::RIGHT).size; right.width += m_bar.borders.at(edge::RIGHT).size;
right.height += m_bar.size.h; right.height += m_bar.size.h;
m_log.trace("renderer: copy pixmap (clear=%i)", clear); // Calculate the area that was reserved so that we
// can clear any previous content drawn at the same location
xcb_rectangle_t clear_area{0, 0, 0U, 0U};
if (m_cleararea.size && m_cleararea.side == edge::RIGHT) {
clear_area.x = m_bar.size.w - m_cleararea.size;
clear_area.y = 0;
clear_area.width = m_cleararea.size;
clear_area.height = m_bar.size.h;
} else if (m_cleararea.size && m_cleararea.side == edge::LEFT) {
clear_area.x = m_rect.x;
clear_area.y = m_rect.y;
clear_area.width = m_cleararea.size;
clear_area.height = m_rect.height;
} else if (m_cleararea.size && m_cleararea.side == edge::TOP) {
clear_area.x = m_rect.x;
clear_area.y = m_rect.y;
clear_area.width = m_rect.width;
clear_area.height = m_cleararea.size;
} else if (m_cleararea.size && m_cleararea.side == edge::TOP) {
clear_area.x = m_rect.x;
clear_area.y = m_rect.y + m_rect.height - m_cleararea.size;
clear_area.width = m_rect.width;
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;
}
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_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); m_log.trace_x("renderer: draw top border (%lupx, %08x)", top.height, m_bar.borders.at(edge::TOP).color);
@ -234,6 +268,9 @@ void renderer::flush(bool clear) {
void renderer::reserve_space(edge side, uint16_t w) { void renderer::reserve_space(edge side, uint16_t w) {
m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast<uint8_t>(side), w); m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast<uint8_t>(side), w);
m_cleararea.side = side;
m_cleararea.size = w;
switch (side) { switch (side) {
case edge::NONE: case edge::NONE:
break; break;

View File

@ -52,6 +52,22 @@ tray_client::~tray_client() {
xembed::unembed(m_connection, window(), m_connection.root()); xembed::unembed(m_connection, window(), m_connection.root());
} }
uint16_t tray_client::width() const {
return m_width;
}
uint16_t tray_client::height() const {
return m_height;
}
void tray_client::clear_window() const {
try {
m_connection.clear_area_checked(1, window(), 0, 0, width(), height());
} catch (const xpp::x::error::window& err) {
// ignore
}
}
/** /**
* Match given window against client window * Match given window against client window
*/ */
@ -399,8 +415,19 @@ void tray_manager::reconfigure_bg(bool realloc) {
return m_log.err("Failed to get root pixmap for tray background (realloc=%i)", realloc); return m_log.err("Failed to get root pixmap for tray background (realloc=%i)", realloc);
} }
m_log.trace("tray: rootpixmap=%x (%dx%d+%d+%d), tray=%x, pixmap=%x, gc=%x", m_rootpixmap.pixmap, m_rootpixmap.width, if (realloc) {
m_rootpixmap.height, m_rootpixmap.x, m_rootpixmap.y, m_tray, m_pixmap, m_gc); // 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.pixmap),
m_rootpixmap.width,
m_rootpixmap.height,
m_rootpixmap.x,
m_rootpixmap.y,
m_connection.id(m_tray),
m_connection.id(m_pixmap),
m_connection.id(m_gc));
// clang-format on
}
m_prevwidth = w; m_prevwidth = w;
m_prevheight = h; m_prevheight = h;
@ -411,6 +438,14 @@ void tray_manager::reconfigure_bg(bool realloc) {
auto px = m_rootpixmap.x + x; auto px = m_rootpixmap.x + x;
auto py = m_rootpixmap.y + y; auto py = m_rootpixmap.y + y;
// Make sure we don't try to copy void content
if (px + w > m_rootpixmap.width) {
w -= px + w - m_rootpixmap.width;
}
if (py + h > m_rootpixmap.height) {
h -= py + h - m_rootpixmap.height;
}
if (realloc) { if (realloc) {
vector<uint8_t> image_data; vector<uint8_t> image_data;
uint8_t image_depth; uint8_t image_depth;
@ -461,10 +496,10 @@ void tray_manager::refresh_window() {
draw_util::fill(m_connection, m_pixmap, m_gc, 0, 0, width, height); draw_util::fill(m_connection, m_pixmap, m_gc, 0, 0, width, height);
} }
m_connection.clear_area(1, 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) {
m_connection.clear_area(1, client->window(), 0, 0, m_opts.width, height); client->clear_window();
} }
m_connection.flush(); m_connection.flush();
@ -473,9 +508,9 @@ void tray_manager::refresh_window() {
/** /**
* Redraw window * Redraw window
*/ */
void tray_manager::redraw_window() { void tray_manager::redraw_window(bool realloc_bg) {
m_log.info("Redraw tray container (id=%s)", m_connection.id(m_tray)); m_log.info("Redraw tray container (id=%s)", m_connection.id(m_tray));
reconfigure_bg(true); reconfigure_bg(realloc_bg);
refresh_window(); refresh_window();
} }
@ -501,7 +536,9 @@ void tray_manager::create_window() {
<< cw_pos(calculate_x(calculate_w()), calculate_y()) << cw_pos(calculate_x(calculate_w()), calculate_y())
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT) << cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
<< cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED) << cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED)
<< cw_params_event_mask(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY) << cw_params_event_mask(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
|XCB_EVENT_MASK_STRUCTURE_NOTIFY
|XCB_EVENT_MASK_EXPOSURE)
<< cw_params_override_redirect(true); << cw_params_override_redirect(true);
// clang-format on // clang-format on
@ -880,7 +917,6 @@ int tray_manager::mapped_clients() const {
*/ */
void tray_manager::handle(const evt::expose& evt) { void tray_manager::handle(const evt::expose& evt) {
if (m_activated && !m_clients.empty() && evt->count == 0) { if (m_activated && !m_clients.empty() && evt->count == 0) {
reconfigure_window();
redraw_window(); redraw_window();
} }
} }
@ -1001,11 +1037,11 @@ void tray_manager::handle(const evt::property_notify& evt) {
if (!m_activated) { if (!m_activated) {
return; return;
} else if (evt->atom == _XROOTMAP_ID) { } else if (evt->atom == _XROOTMAP_ID) {
redraw_window(); redraw_window(true);
} else if (evt->atom == _XSETROOT_ID) { } else if (evt->atom == _XSETROOT_ID) {
redraw_window(); redraw_window(true);
} else if (evt->atom == ESETROOT_PMAP_ID) { } else if (evt->atom == ESETROOT_PMAP_ID) {
redraw_window(); redraw_window(true);
} else if (evt->atom == _XEMBED_INFO) { } else if (evt->atom == _XEMBED_INFO) {
auto client = find_client(evt->window); auto client = find_client(evt->window);