fix(renderer): Handling of missing root pixmap (#1608)

Polybar had issues when there is no background set or set by a tool like imagemagick which doesn't add the root pixmap to the root window properties.

There's not much we can do about it, but at least polybar doesn't crash anymore.

Fixes #1582 
Fixes #1585 

* fix(tray_manager): only enable transparency if neccessary

Previously, we always enabled transparency

* fix(background_manager): avoid needless fetching

* fix(renderer): move logging message to correct place

* fix(background_manager): handle dummy pixmap (_XSETROOT_ID) right

* fix(background_manager): more initialization + don't free on error

Freeing on error is incorrect, since we could still be called again later in
which case we still need the resources.

* fix(background_manager): add more infos to trace logs

* fix(background): correct typo (XROOTMAP -> XROOTPMAP)

* fix(background_manager): do not report "no background" as error

* style(background_manager): use braces for if

Co-Authored-By: bennofs <benno.fuenfstueck@gmail.com>

* fix(background_manager): better error message for dummy pixmap

Co-Authored-By: bennofs <benno.fuenfstueck@gmail.com>

* style(background): some more style fixes

* fix(connection): initialize pixmap in all cases in root_pixmap()

* style(connection): improve readability using early return
This commit is contained in:
Benno Fünfstück 2019-01-17 14:22:48 +01:00 committed by Patrick Ziegler
parent 01be9b3504
commit f74c524fff
6 changed files with 56 additions and 28 deletions

View File

@ -40,7 +40,7 @@ extern xcb_atom_t _NET_SYSTEM_TRAY_COLORS;
extern xcb_atom_t WM_TAKE_FOCUS; extern xcb_atom_t WM_TAKE_FOCUS;
extern xcb_atom_t Backlight; extern xcb_atom_t Backlight;
extern xcb_atom_t BACKLIGHT; extern xcb_atom_t BACKLIGHT;
extern xcb_atom_t _XROOTMAP_ID; extern xcb_atom_t _XROOTPMAP_ID;
extern xcb_atom_t _XSETROOT_ID; extern xcb_atom_t _XSETROOT_ID;
extern xcb_atom_t ESETROOT_PMAP_ID; extern xcb_atom_t ESETROOT_PMAP_ID;
extern xcb_atom_t _COMPTON_SHADOW; extern xcb_atom_t _COMPTON_SHADOW;

View File

@ -163,12 +163,11 @@ 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_pseudo_transparency = m_conf.get<bool>("settings", "pseudo-transparency", m_pseudo_transparency); m_pseudo_transparency = m_conf.get<bool>("settings", "pseudo-transparency", m_pseudo_transparency);
if (m_pseudo_transparency) { if (m_pseudo_transparency) {
m_log.trace("Activate root background manager");
m_background = background.observe(m_bar.outer_area(false), m_window); m_background = background.observe(m_bar.outer_area(false), m_window);
} }

View File

@ -33,7 +33,7 @@ xcb_atom_t _NET_SYSTEM_TRAY_COLORS;
xcb_atom_t WM_TAKE_FOCUS; xcb_atom_t WM_TAKE_FOCUS;
xcb_atom_t Backlight; xcb_atom_t Backlight;
xcb_atom_t BACKLIGHT; xcb_atom_t BACKLIGHT;
xcb_atom_t _XROOTMAP_ID; xcb_atom_t _XROOTPMAP_ID;
xcb_atom_t _XSETROOT_ID; xcb_atom_t _XSETROOT_ID;
xcb_atom_t ESETROOT_PMAP_ID; xcb_atom_t ESETROOT_PMAP_ID;
xcb_atom_t _COMPTON_SHADOW; xcb_atom_t _COMPTON_SHADOW;
@ -72,7 +72,7 @@ cached_atom ATOMS[36] = {
{"WM_TAKE_FOCUS", sizeof("WM_TAKE_FOCUS") - 1, &WM_TAKE_FOCUS}, {"WM_TAKE_FOCUS", sizeof("WM_TAKE_FOCUS") - 1, &WM_TAKE_FOCUS},
{"Backlight", sizeof("Backlight") - 1, &Backlight}, {"Backlight", sizeof("Backlight") - 1, &Backlight},
{"BACKLIGHT", sizeof("BACKLIGHT") - 1, &BACKLIGHT}, {"BACKLIGHT", sizeof("BACKLIGHT") - 1, &BACKLIGHT},
{"_XROOTMAP_ID", sizeof("_XROOTMAP_ID") - 1, &_XROOTMAP_ID}, {"_XROOTPMAP_ID", sizeof("_XROOTPMAP_ID") - 1, &_XROOTPMAP_ID},
{"_XSETROOT_ID", sizeof("_XSETROOT_ID") - 1, &_XSETROOT_ID}, {"_XSETROOT_ID", sizeof("_XSETROOT_ID") - 1, &_XSETROOT_ID},
{"ESETROOT_PMAP_ID", sizeof("ESETROOT_PMAP_ID") - 1, &ESETROOT_PMAP_ID}, {"ESETROOT_PMAP_ID", sizeof("ESETROOT_PMAP_ID") - 1, &ESETROOT_PMAP_ID},
{"_COMPTON_SHADOW", sizeof("_COMPTON_SHADOW") - 1, &_COMPTON_SHADOW}, {"_COMPTON_SHADOW", sizeof("_COMPTON_SHADOW") - 1, &_COMPTON_SHADOW},

View File

@ -81,9 +81,14 @@ void background_manager::fetch_root_pixmap() {
try { try {
if (!m_connection.root_pixmap(&pixmap, &pixmap_depth, &pixmap_geom)) { if (!m_connection.root_pixmap(&pixmap, &pixmap_depth, &pixmap_geom)) {
free_resources(); return m_log.warn("background_manager: Failed to get root pixmap, default to black (is there a wallpaper?)");
return m_log.err("background_manager: Failed to get root pixmap for background (realloc=%i)", realloc);
}; };
m_log.trace("background_manager: root pixmap (%d:%d) %dx%d+%d+%d", pixmap, pixmap_depth,
pixmap_geom.width, pixmap_geom.height, pixmap_geom.x, pixmap_geom.y);
if (pixmap_depth == 1 && pixmap_geom.width == 1 && pixmap_geom.height == 1) {
return m_log.err("background_manager: Cannot find root pixmap, try a different tool to set the desktop background");
}
for (auto it = m_slices.begin(); it != m_slices.end(); ) { for (auto it = m_slices.begin(); it != m_slices.end(); ) {
auto slice = it->lock(); auto slice = it->lock();
@ -98,7 +103,7 @@ void background_manager::fetch_root_pixmap() {
auto src_y = math_util::cap(translated->dst_y, pixmap_geom.y, int16_t(pixmap_geom.y + pixmap_geom.height)); auto src_y = math_util::cap(translated->dst_y, pixmap_geom.y, int16_t(pixmap_geom.y + pixmap_geom.height));
auto w = math_util::cap(slice->m_rect.width, uint16_t(0), uint16_t(pixmap_geom.width - (src_x - pixmap_geom.x))); auto w = math_util::cap(slice->m_rect.width, uint16_t(0), uint16_t(pixmap_geom.width - (src_x - pixmap_geom.x)));
auto h = math_util::cap(slice->m_rect.height, uint16_t(0), uint16_t(pixmap_geom.height - (src_y - pixmap_geom.y))); auto h = math_util::cap(slice->m_rect.height, uint16_t(0), uint16_t(pixmap_geom.height - (src_y - pixmap_geom.y)));
m_log.trace("background_manager: Copying from root pixmap (%d) %dx%d+%dx%d", pixmap, w, h, src_x, src_y); m_log.trace("background_manager: Copying from root pixmap (%d:%d) %dx%d+%d+%d", pixmap, pixmap_depth, w, h, src_x, src_y);
m_connection.copy_area_checked(pixmap, slice->m_pixmap, slice->m_gcontext, src_x, src_y, 0, 0, w, h); m_connection.copy_area_checked(pixmap, slice->m_pixmap, slice->m_gcontext, src_x, src_y, 0, 0, w, h);
it++; it++;
@ -119,15 +124,22 @@ void background_manager::fetch_root_pixmap() {
void background_manager::handle(const evt::property_notify& evt) { void background_manager::handle(const evt::property_notify& evt) {
// if there are no slices to observe, don't do anything // if there are no slices to observe, don't do anything
if(m_slices.empty()) return; if(m_slices.empty()) {
return;
}
if (evt->atom == _XROOTMAP_ID || evt->atom == _XSETROOT_ID || evt->atom == ESETROOT_PMAP_ID) { if (evt->atom == _XROOTPMAP_ID || evt->atom == _XSETROOT_ID || evt->atom == ESETROOT_PMAP_ID) {
fetch_root_pixmap(); fetch_root_pixmap();
m_sig.emit(signals::ui::update_background()); m_sig.emit(signals::ui::update_background());
} }
} }
bool background_manager::on(const signals::ui::update_geometry&) { bool background_manager::on(const signals::ui::update_geometry&) {
// if there are no slices to observe, don't do anything
if(m_slices.empty()) {
return false;
}
fetch_root_pixmap(); fetch_root_pixmap();
m_sig.emit(signals::ui::update_background()); m_sig.emit(signals::ui::update_background());
return false; return false;
@ -159,8 +171,9 @@ void bg_slice::allocate_resources(const logger& log, xcb_visualtype_t* visual) {
if(m_gcontext == XCB_NONE) { if(m_gcontext == XCB_NONE) {
log.trace("background_manager: Allocating graphics context"); log.trace("background_manager: Allocating graphics context");
unsigned int mask = XCB_GC_GRAPHICS_EXPOSURES; auto black_pixel = m_connection.screen()->black_pixel;
unsigned int value_list[1] = {0}; unsigned int mask = XCB_GC_GRAPHICS_EXPOSURES | XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
unsigned int value_list[3] = {black_pixel, black_pixel, 0};
m_gcontext = m_connection.generate_id(); m_gcontext = m_connection.generate_id();
m_connection.create_gc(m_gcontext, m_pixmap, mask, value_list); m_connection.create_gc(m_gcontext, m_pixmap, mask, value_list);
} }
@ -169,6 +182,10 @@ void bg_slice::allocate_resources(const logger& log, xcb_visualtype_t* visual) {
log.trace("background_manager: Allocating cairo surface"); log.trace("background_manager: Allocating cairo surface");
m_surface = make_unique<cairo::xcb_surface>(m_connection, m_pixmap, visual, m_rect.width, m_rect.height); m_surface = make_unique<cairo::xcb_surface>(m_connection, m_pixmap, visual, m_rect.width, m_rect.height);
} }
// fill (to provide a default in the case that fetching the background fails)
xcb_rectangle_t rect{0, 0, m_rect.width, m_rect.height};
m_connection.poly_fill_rectangle(m_pixmap, m_gcontext, 1, &rect);
} }
void bg_slice::free_resources() { void bg_slice::free_resources() {

View File

@ -182,29 +182,40 @@ xcb_visualtype_t* connection::visual_type_for_id(xcb_screen_t* screen, xcb_visua
* Query root window pixmap * Query root window pixmap
*/ */
bool connection::root_pixmap(xcb_pixmap_t* pixmap, int* depth, xcb_rectangle_t* rect) { bool connection::root_pixmap(xcb_pixmap_t* pixmap, int* depth, xcb_rectangle_t* rect) {
const xcb_atom_t pixmap_properties[3]{ESETROOT_PMAP_ID, _XROOTMAP_ID, _XSETROOT_ID}; *pixmap = XCB_NONE; // default value if getting the root pixmap fails
/*
* We try multiple properties for the root pixmap here because I am not 100% sure
* if all programs set them the same way. We might be able to just use _XSETROOT_ID
* but keeping the other as fallback should not hurt (if it does, feel free to remove).
*
* see https://metacpan.org/pod/X11::Protocol::XSetRoot#ROOT-WINDOW-PROPERTIES for description of the properties
* the properties here are ordered by reliability:
* _XSETROOT_ID: this is usually a dummy 1x1 pixmap only for resource managment, use only as last resort
* ESETROOT_PMAP_ID: according to the link above, this should usually by equal to _XROOTPMAP_ID
* _XROOTPMAP_ID: this appears to be the "correct" property to use? if available, use this
*/
const xcb_atom_t pixmap_properties[3]{_XROOTPMAP_ID, ESETROOT_PMAP_ID, _XSETROOT_ID};
for (auto&& property : pixmap_properties) { for (auto&& property : pixmap_properties) {
try { try {
auto prop = get_property(false, screen()->root, property, XCB_ATOM_PIXMAP, 0L, 1L); auto prop = get_property(false, screen()->root, property, XCB_ATOM_PIXMAP, 0L, 1L);
if (prop->format == 32 && prop->value_len == 1) { if (prop->format == 32 && prop->value_len == 1) {
*pixmap = *prop.value<xcb_pixmap_t>().begin(); *pixmap = *prop.value<xcb_pixmap_t>().begin();
} }
} catch (const exception& err) {
continue; if (*pixmap) {
} auto geom = get_geometry(*pixmap);
} *depth = geom->depth;
if (*pixmap) { rect->width = geom->width;
try { rect->height = geom->height;
auto geom = get_geometry(*pixmap); rect->x = geom->x;
*depth = geom->depth; rect->y = geom->y;
rect->width = geom->width; return true;
rect->height = geom->height; }
rect->x = geom->x;
rect->y = geom->y;
return true;
} catch (const exception& err) { } catch (const exception& err) {
*pixmap = XCB_NONE; *pixmap = XCB_NONE;
*rect = xcb_rectangle_t{0, 0, 0U, 0U}; *rect = xcb_rectangle_t{0, 0, 0U, 0U};
continue;
} }
} }
return false; return false;

View File

@ -133,7 +133,8 @@ 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) != 1) { if (color_util::alpha_channel<unsigned char>(m_opts.background) != 255) {
m_log.trace("tray: enable transparency");
m_opts.transparent = true; m_opts.transparent = true;
} }
@ -996,7 +997,7 @@ void tray_manager::handle(const evt::selection_clear& evt) {
void tray_manager::handle(const evt::property_notify& evt) { 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 == _XROOTPMAP_ID) {
redraw_window(true); redraw_window(true);
} else if (evt->atom == _XSETROOT_ID) { } else if (evt->atom == _XSETROOT_ID) {
redraw_window(true); redraw_window(true);