From 94298741b6de08e79131006504850820bdff9024 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Mon, 31 Oct 2016 01:41:14 +0100 Subject: [PATCH] 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 --- examples/config | 2 + examples/config.bspwm | 6 +- examples/config.i3 | 2 + include/components/bar.hpp | 106 +++++++++++++++++++---- include/components/types.hpp | 6 +- include/components/x11/atoms.hpp | 10 ++- include/components/x11/tray.hpp | 144 +++++++++++++++++++++---------- 7 files changed, 205 insertions(+), 71 deletions(-) diff --git a/examples/config b/examples/config index 20a06973..224e1dce 100644 --- a/examples/config +++ b/examples/config @@ -38,6 +38,8 @@ modules-center = date modules-right = volume memory cpu tray-position = right +tray-background = #33ffffff +tray-padding = 4 [module/memory] diff --git a/examples/config.bspwm b/examples/config.bspwm index 8ec89473..0ef0601d 100644 --- a/examples/config.bspwm +++ b/examples/config.bspwm @@ -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 diff --git a/examples/config.i3 b/examples/config.i3 index 9dd838cd..1459432e 100644 --- a/examples/config.i3 +++ b/examples/config.i3 @@ -35,6 +35,8 @@ modules-left = i3 modules-right = xbacklight date tray-position = right +tray-background = #33ffffff +tray-padding = 4 [module/i3] diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 1ce813f1..0b5535e2 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -106,9 +106,12 @@ class bar : public xpp::event::sink(bs, "background", m_bar.background.hex_to_rgba())); - m_bar.foreground = color::parse(m_conf.get(bs, "foreground", m_bar.foreground.hex_to_rgba())); - m_bar.linecolor = color::parse(m_conf.get(bs, "linecolor", m_bar.linecolor.hex_to_rgba())); + m_bar.background = + color::parse(m_conf.get(bs, "background", m_bar.background.hex_to_rgba())); + m_bar.foreground = + color::parse(m_conf.get(bs, "foreground", m_bar.foreground.hex_to_rgba())); + m_bar.linecolor = + color::parse(m_conf.get(bs, "linecolor", m_bar.linecolor.hex_to_rgba())); // }}} // Set border values {{{ @@ -398,6 +401,8 @@ class bar : public xpp::event::sink 24) { - m_tray.spacing = (m_tray.height - 24) / 2; - m_tray.height = 24; + auto maxsize = m_conf.get(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(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(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(bs, "tray-padding", 0); + + // Add user-defiend offset + auto offset_x_def = m_conf.get(bs, "tray-offset-x", ""); + auto offset_y_def = m_conf.get(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(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 %s)", static_cast(gc_), color_.hex_to_rgba(), color_.hex_to_rgb()); + m_log.trace_x("bar: color_change(%i, %s -> %s)", static_cast(gc_), color_.hex_to_rgba(), + color_.hex_to_rgb()); if (gc_ == gc::FG) { m_fontmanager->allocate_color(color_); diff --git a/include/components/types.hpp b/include/components/types.hpp index 69a8eed4..c380b8c3 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -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 { diff --git a/include/components/x11/atoms.hpp b/include/components/x11/atoms.hpp index da4534a5..8427851c 100644 --- a/include/components/x11/atoms.hpp +++ b/include/components/x11/atoms.hpp @@ -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}, diff --git a/include/components/x11/tray.hpp b/include/components/x11/tray.hpp index 280f628a..3847cf2e 100644 --- a/include/components/x11/tray.hpp +++ b/include/components/x11/tray.hpp @@ -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() == 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(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(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); } }