tray: Align center and extend configuration

- The tray can now be centered in the bar
- Set window atoms:
  * _NET_SYSTEM_TRAY_COLORS
  * _NET_SYSTEM_TRAY_VISUAL
- New parameters added:
  * tray-background: Define background color
  * tray-offset-x: X position offset
  * tray-offset-y: Y position offset
  * tray-padding: Add spacing on the side of each icon
  * tray-maxsize: Size limit for tray icons, default: 16
  * tray-scale: Icon zoom, default: 1.0
  * tray-detached: The bar will ignore tray updates
This commit is contained in:
Michael Carlberg 2016-10-31 01:41:14 +01:00
parent dd7967dede
commit 94298741b6
7 changed files with 205 additions and 71 deletions

View File

@ -38,6 +38,8 @@ modules-center = date
modules-right = volume memory cpu
tray-position = right
tray-background = #33ffffff
tray-padding = 4
[module/memory]

View File

@ -33,11 +33,13 @@ font-0 = tamzen:size=9;1
font-1 = siji:pixelsize=10;0
font-2 = unifont:size=6;-1
modules-left = mpd
modules-center = bspwm
modules-left = bspwm
modules-center = mpd
modules-right = volume memory cpu date
tray-position = right
tray-background = #33ffffff
tray-padding = 4
wm-restack = bspwm

View File

@ -35,6 +35,8 @@ modules-left = i3
modules-right = xbacklight date
tray-position = right
tray-background = #33ffffff
tray-padding = 4
[module/i3]

View File

@ -106,9 +106,12 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
// }}}
// Set bar colors {{{
m_bar.background = color::parse(m_conf.get<string>(bs, "background", m_bar.background.hex_to_rgba()));
m_bar.foreground = color::parse(m_conf.get<string>(bs, "foreground", m_bar.foreground.hex_to_rgba()));
m_bar.linecolor = color::parse(m_conf.get<string>(bs, "linecolor", m_bar.linecolor.hex_to_rgba()));
m_bar.background =
color::parse(m_conf.get<string>(bs, "background", m_bar.background.hex_to_rgba()));
m_bar.foreground =
color::parse(m_conf.get<string>(bs, "foreground", m_bar.foreground.hex_to_rgba()));
m_bar.linecolor =
color::parse(m_conf.get<string>(bs, "linecolor", m_bar.linecolor.hex_to_rgba()));
// }}}
// Set border values {{{
@ -398,6 +401,8 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
m_tray.align = alignment::LEFT;
else if (tray_position == "right")
m_tray.align = alignment::RIGHT;
else if (tray_position == "center")
m_tray.align = alignment::CENTER;
else
m_tray.align = alignment::NONE;
} catch (const key_error& err) {
@ -405,30 +410,76 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
}
if (m_tray.align != alignment::NONE) {
m_tray.background = m_bar.background.value();
m_tray.height = m_bar.height;
m_tray.height -= m_borders.at(border::BOTTOM).size;
m_tray.height -= m_borders.at(border::TOP).size;
m_tray.height_fill = m_tray.height;
if (m_tray.height % 2 != 0) {
m_tray.height--;
}
if (m_tray.height > 24) {
m_tray.spacing = (m_tray.height - 24) / 2;
m_tray.height = 24;
auto maxsize = m_conf.get<int>(bs, "tray-maxsize", 16);
if (m_tray.height > maxsize) {
m_tray.spacing += (m_tray.height - maxsize) / 2;
m_tray.height = maxsize;
}
m_tray.width = m_tray.height;
m_tray.orig_y = m_bar.y + m_borders.at(border::TOP).size;
if (m_tray.align == alignment::RIGHT)
m_tray.orig_x = m_bar.x + m_bar.width - m_borders.at(border::RIGHT).size;
else
m_tray.orig_x = m_bar.x + m_borders.at(border::LEFT).size;
}
// Apply user-defined scaling
auto scale = m_conf.get<float>(bs, "tray-scale", 1.0);
m_tray.width *= scale;
m_tray.height_fill *= scale;
m_tray.sibling = m_window;
if (m_tray.align == alignment::RIGHT) {
m_tray.orig_x = m_bar.x + m_bar.width - m_borders.at(border::RIGHT).size;
} else if (m_tray.align == alignment::LEFT) {
m_tray.orig_x = m_bar.x + m_borders.at(border::LEFT).size;
} else if (m_tray.align == alignment::CENTER) {
m_tray.orig_x = center_x() - (m_tray.width / 2);
}
// Set user-defined background color
try {
auto tray_background = m_conf.get<string>(bs, "tray-background");
m_tray.background = color::parse(tray_background).value();
} catch (const key_error&) {
m_tray.background = m_bar.background.value();
}
// Add user-defined padding
m_tray.spacing += m_conf.get<int>(bs, "tray-padding", 0);
// Add user-defiend offset
auto offset_x_def = m_conf.get<string>(bs, "tray-offset-x", "");
auto offset_y_def = m_conf.get<string>(bs, "tray-offset-y", "");
auto offset_x = std::atoi(offset_x_def.c_str());
auto offset_y = std::atoi(offset_y_def.c_str());
if (offset_x != 0 && offset_x_def.find("%") != string::npos) {
offset_x = math_util::percentage_to_value(offset_x, m_bar.monitor->w);
offset_x -= m_tray.width / 2;
}
if (offset_y != 0 && offset_y_def.find("%") != string::npos) {
offset_y = math_util::percentage_to_value(offset_y, m_bar.monitor->h);
offset_y -= m_tray.width / 2;
}
m_tray.orig_x += offset_x;
m_tray.orig_y += offset_y;
// Add tray update callback unless explicitly disabled
if (!m_conf.get<bool>(bs, "tray-detached", false)) {
g_signals::tray::report_slotcount = bind(&bar::on_tray_report, this, placeholders::_1);
}
// Put the tray next to the bar in the window stack
m_tray.sibling = m_window;
}
// }}}
// Connect signal handlers {{{
@ -447,9 +498,6 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
g_signals::parser::unicode_text_write = bind(&bar::draw_character, this, placeholders::_1);
// clang-format on
if (m_tray.align != alignment::NONE)
g_signals::tray::report_slotcount = bind(&bar::on_tray_report, this, placeholders::_1);
// }}}
m_connection.attach_sink(this, 1);
@ -674,6 +722,28 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
} // }}}
protected:
/**
* Get the horizontal center pos
*/
int center_x() {
int x = m_bar.x;
x += m_bar.width;
x -= m_borders[border::RIGHT].size;
x += m_borders[border::LEFT].size;
x /= 2;
return x;
}
/**
* Get the inner width of the bar
*/
int width_inner() {
auto w = m_bar.width;
w -= m_borders[border::RIGHT].size;
w -= m_borders[border::LEFT].size;
return w;
}
/**
* Handle alignment update
*/
@ -778,8 +848,8 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
* Handle color change
*/
void on_color_change(gc gc_, color color_) { //{{{
m_log.trace_x(
"bar: color_change(%i, %s -> %s)", static_cast<int>(gc_), color_.hex_to_rgba(), color_.hex_to_rgb());
m_log.trace_x("bar: color_change(%i, %s -> %s)", static_cast<int>(gc_), color_.hex_to_rgba(),
color_.hex_to_rgb());
if (gc_ == gc::FG) {
m_fontmanager->allocate_color(color_);

View File

@ -69,9 +69,11 @@ struct tray_settings {
orig_y = o.orig_y;
width = o.width;
height = o.height;
height_fill = o.height_fill;
spacing = o.spacing;
slots = o.slots;
sibling = o.sibling;
bg_pixmap = o.bg_pixmap;
return *this;
}
@ -81,9 +83,11 @@ struct tray_settings {
int16_t orig_y{0};
uint16_t width{0};
uint16_t height{0};
uint16_t height_fill{0};
uint16_t spacing{0};
uint16_t slots{0};
uint32_t sibling;
uint32_t sibling{0};
uint32_t bg_pixmap{0};
};
struct border_settings {

View File

@ -26,16 +26,18 @@ static xcb_atom_t WM_PROTOCOLS;
static xcb_atom_t WM_DELETE_WINDOW;
static xcb_atom_t _XEMBED;
static xcb_atom_t _XEMBED_INFO;
static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE;
static xcb_atom_t MANAGER;
static xcb_atom_t WM_STATE;
static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE;
static xcb_atom_t _NET_SYSTEM_TRAY_ORIENTATION;
static xcb_atom_t _NET_SYSTEM_TRAY_VISUAL;
static xcb_atom_t _NET_SYSTEM_TRAY_COLORS;
static xcb_atom_t WM_TAKE_FOCUS;
static xcb_atom_t Backlight;
static xcb_atom_t BACKLIGHT;
// clang-format off
static cached_atom ATOMS[24] = {
static cached_atom ATOMS[26] = {
{"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &_NET_WM_NAME},
{"_NET_WM_DESKTOP", sizeof("_NET_WM_DESKTOP") - 1, &_NET_WM_DESKTOP},
{"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1, &_NET_WM_WINDOW_TYPE},
@ -53,10 +55,12 @@ static cached_atom ATOMS[24] = {
{"WM_DELETE_WINDOW", sizeof("WM_DELETE_WINDOW") - 1, &WM_DELETE_WINDOW},
{"_XEMBED", sizeof("_XEMBED") - 1, &_XEMBED},
{"_XEMBED_INFO", sizeof("_XEMBED_INFO") - 1, &_XEMBED_INFO},
{"_NET_SYSTEM_TRAY_OPCODE", sizeof("_NET_SYSTEM_TRAY_OPCODE") - 1, &_NET_SYSTEM_TRAY_OPCODE},
{"MANAGER", sizeof("MANAGER") - 1, &MANAGER},
{"WM_STATE", sizeof("WM_STATE") - 1, &WM_STATE},
{"_NET_SYSTEM_TRAY_OPCODE", sizeof("_NET_SYSTEM_TRAY_OPCODE") - 1, &_NET_SYSTEM_TRAY_OPCODE},
{"_NET_SYSTEM_TRAY_ORIENTATION", sizeof("_NET_SYSTEM_TRAY_ORIENTATION") - 1, &_NET_SYSTEM_TRAY_ORIENTATION},
{"_NET_SYSTEM_TRAY_VISUAL", sizeof("_NET_SYSTEM_TRAY_VISUAL") - 1, &_NET_SYSTEM_TRAY_VISUAL},
{"_NET_SYSTEM_TRAY_COLORS", sizeof("_NET_SYSTEM_TRAY_COLORS") - 1, &_NET_SYSTEM_TRAY_COLORS},
{"WM_TAKE_FOCUS", sizeof("WM_TAKE_FOCUS") - 1, &WM_TAKE_FOCUS},
{"Backlight", sizeof("Backlight") - 1, &Backlight},
{"BACKLIGHT", sizeof("BACKLIGHT") - 1, &BACKLIGHT},

View File

@ -9,6 +9,7 @@
#include "components/types.hpp"
#include "components/x11/connection.hpp"
#include "components/x11/xembed.hpp"
#include "utils/color.hpp"
#include "utils/memory.hpp"
#include "utils/process.hpp"
@ -141,6 +142,7 @@ class traymanager
try {
create_window();
set_wmhints();
set_traycolors();
} catch (const std::exception& err) {
m_log.err(err.what());
m_log.err("Cannot activate traymanager... failed to setup window");
@ -200,14 +202,14 @@ class traymanager
g_signals::bar::visibility_change = nullptr;
}
m_log.trace("tray: Unembed all clients");
m_clients.clear();
if (m_connection.get_selection_owner_unchecked(m_atom).owner<xcb_window_t>() == m_tray) {
m_log.trace("tray: Unset selection owner");
m_connection.set_selection_owner(XCB_NONE, m_atom, XCB_CURRENT_TIME);
}
m_log.trace("tray: Unembed clients");
m_clients.clear();
if (m_tray != XCB_NONE) {
if (m_mapped) {
m_log.trace("tray: Unmap window");
@ -246,30 +248,37 @@ class traymanager
}
}
if (g_signals::tray::report_slotcount)
g_signals::tray::report_slotcount(mapped_clients);
m_settings.slots = mapped_clients;
if (!width && m_mapped) {
m_connection.unmap_window_checked(m_tray);
return;
} else if (width && !m_mapped) {
m_connection.map_window_checked(m_tray);
m_lastwidth = 0;
return;
} else if (!width) {
return;
try {
if (g_signals::tray::report_slotcount)
g_signals::tray::report_slotcount(mapped_clients);
if (!width && m_mapped) {
m_connection.unmap_window_checked(m_tray);
return;
} else if (width && !m_mapped) {
m_connection.map_window_checked(m_tray);
m_lastwidth = 0;
return;
} else if (!width) {
return;
} else if ((width += m_settings.spacing) == m_lastwidth) {
return;
}
m_lastwidth = width;
// clear window to get rid of frozen artifacts
m_connection.clear_area(1, m_tray, 0, 0, 0, 0);
// update window
const uint32_t val[2]{static_cast<uint32_t>(calculate_x(width)), width};
m_connection.configure_window(m_tray, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_WIDTH, val);
} catch (const std::exception& err) {
m_log.err("Failed to configure tray window (%s)", err.what());
}
if ((width += m_settings.spacing) == m_lastwidth) {
return;
}
m_lastwidth = width;
// update window
const uint32_t val[2]{static_cast<uint32_t>(calculate_x()), width};
m_connection.configure_window(m_tray, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_WIDTH, val);
// reposition clients
uint32_t pos_x = m_settings.spacing;
for (auto&& client : m_clients) {
@ -315,10 +324,12 @@ class traymanager
/**
* Calculate the tray window's horizontal position
*/
int16_t calculate_x() const {
int16_t calculate_x(uint32_t width) const {
auto x = m_settings.orig_x;
if (m_settings.align == alignment::RIGHT)
x -= ((m_settings.width + m_settings.spacing) * m_clients.size() + m_settings.spacing);
else if (m_settings.align == alignment::CENTER)
x -= (width / 2) - (m_settings.width / 2);
return x;
}
@ -354,31 +365,29 @@ class traymanager
* Create tray window
*/
void create_window() {
auto x = calculate_x();
auto x = calculate_x(0);
auto y = calculate_y();
auto scr = m_connection.screen();
m_tray = m_connection.generate_id();
m_log.trace("tray: Create tray window %s, (%ix%i+%i+%i)", m_connection.id(m_tray),
m_settings.width, m_settings.height, x, y);
auto scr = m_connection.screen();
m_settings.width, m_settings.height_fill, x, y);
const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
const uint32_t values[3]{m_settings.background, true,
const uint32_t values[4]{m_settings.background, true,
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_connection.create_window_checked(scr->root_depth, m_tray, scr->root, x, y,
m_settings.width + m_settings.spacing * 2, m_settings.height + m_settings.spacing * 2, 0,
m_settings.width + m_settings.spacing * 2, m_settings.height_fill, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, mask, values);
try {
// Put the tray window above the defined sibling in the window stack
if (m_settings.sibling != XCB_NONE) {
const uint32_t value_mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
const uint32_t value_list[2]{m_settings.sibling, XCB_STACK_MODE_ABOVE};
m_connection.configure_window_checked(m_tray, value_mask, value_list);
m_connection.flush();
m_restacked = true;
}
} catch (const std::exception& err) {
auto id = m_connection.id(m_settings.sibling);
m_log.trace("tray: Failed to put tray above %s in the stack (%s)", id, err.what());
// Put the tray window above the defined sibling in the window stack
if (m_settings.sibling != XCB_NONE) {
const uint32_t value_mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
const uint32_t value_list[2]{m_settings.sibling, XCB_STACK_MODE_ABOVE};
m_connection.configure_window_checked(m_tray, value_mask, value_list);
m_connection.flush();
m_restacked = true;
}
}
@ -415,12 +424,43 @@ class traymanager
m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_tray,
_NET_SYSTEM_TRAY_ORIENTATION, _NET_SYSTEM_TRAY_ORIENTATION, 32, 1, values);
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_VISUAL");
const uint32_t values2[1]{m_connection.screen()->root_visual};
m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_tray,
_NET_SYSTEM_TRAY_ORIENTATION, XCB_ATOM_VISUALID, 32, 1, values2);
m_log.trace("tray: Set window _NET_WM_PID");
int pid = getpid();
m_connection.change_property(
XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
}
/**
* Set color atom used by clients when determing icon theme
*/
void set_traycolors() {
m_log.trace("tray: Set _NET_SYSTEM_TRAY_COLORS to %x", m_settings.background);
auto c = color_util::make_32bit(m_settings.background);
auto r = color_util::red(c);
auto g = color_util::green(c);
auto b = color_util::blue(c);
const uint32_t colors[12] = {
r, g, b, // normal
r, g, b, // error
r, g, b, // warning
r, g, b, // success
};
try {
m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_COLORS,
XCB_ATOM_CARDINAL, 32, 12, colors);
} catch (const std::exception& err) {
m_log.err("Failed to set tray colors");
}
}
/**
* Acquire the systray selection
*/
@ -479,6 +519,13 @@ class traymanager
return m_settings.spacing;
}
/**
* Calculates a client's y position
*/
int calculate_client_ypos() {
return (m_settings.height_fill - m_settings.height) / 2;
}
/**
* Process client docking request
*/
@ -526,11 +573,14 @@ class traymanager
m_connection.change_save_set_checked(XCB_SET_MODE_INSERT, client->window());
m_log.trace("tray: Update tray client event mask");
const uint32_t event_mask[]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_connection.change_window_attributes_checked(client->window(), XCB_CW_EVENT_MASK, event_mask);
const uint32_t event_mask[]{XCB_BACK_PIXMAP_PARENT_RELATIVE,
XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_connection.change_window_attributes_checked(
client->window(), XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK, event_mask);
m_log.trace("tray: Reparent tray client");
m_connection.reparent_window_checked(client->window(), m_tray, m_settings.spacing, m_settings.spacing);
m_connection.reparent_window_checked(
client->window(), m_tray, m_settings.spacing, calculate_client_ypos());
m_log.trace("tray: Configure tray client size");
const uint32_t values[]{m_settings.width, m_settings.height};
@ -623,7 +673,7 @@ class traymanager
auto client = find_client(evt->window);
if (client) {
m_log.trace("tray: Received configure_request for client %s", m_connection.id(evt->window));
client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing,
client->configure_notify(calculate_client_xpos(evt->window), calculate_client_ypos(),
m_settings.width, m_settings.height);
}
}
@ -638,7 +688,7 @@ class traymanager
auto client = find_client(evt->window);
if (client) {
m_log.trace("tray: Received resize_request for client %s", m_connection.id(evt->window));
client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing,
client->configure_notify(calculate_client_xpos(evt->window), calculate_client_ypos(),
m_settings.width, m_settings.height);
}
}