From 23996fdcdc76751dd1c2e8af77a203fe12d66cf6 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Sat, 12 Nov 2016 12:56:39 +0100 Subject: [PATCH] refactor: Tray transparency and code cleaning - Make pseudo-transparency work as intended, fixes #158 - Fill tray pixmap in case a root pixmap is not found - Split up bar bootstrap routine - Formatting (clang-format) --- examples/config | 2 + examples/config.cmake | 2 + include/components/bar.hpp | 35 +- include/components/config.hpp | 2 +- include/components/controller.hpp | 5 +- include/components/types.hpp | 22 - include/x11/graphics.hpp | 2 +- include/x11/tray.hpp | 76 ++- include/x11/window.hpp | 14 +- src/components/bar.cpp | 1034 +++++++++++++++-------------- src/components/controller.cpp | 22 +- src/components/types.cpp | 1 - src/modules/bspwm.cpp | 103 ++- src/x11/graphics.cpp | 9 +- src/x11/tray.cpp | 500 ++++++++------ src/x11/window.cpp | 75 ++- 16 files changed, 1032 insertions(+), 872 deletions(-) delete mode 100644 src/components/types.cpp diff --git a/examples/config b/examples/config index 6cdb50ec..5ec3104f 100644 --- a/examples/config +++ b/examples/config @@ -46,6 +46,8 @@ modules-right = backlight volume memory cpu wlan eth battery temperature date po tray-position = right tray-padding = 4 +;tray-transparent = true +;tray-background = #0063ff ;wm-restack = bspwm diff --git a/examples/config.cmake b/examples/config.cmake index d2f9c07c..413deee6 100644 --- a/examples/config.cmake +++ b/examples/config.cmake @@ -46,6 +46,8 @@ modules-right = @MODULES_RIGHT@ tray-position = right tray-padding = 4 +;tray-transparent = true +;tray-background = #0063ff ;wm-restack = bspwm diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 06967e00..e6b249b2 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -20,13 +20,13 @@ LEMONBUDDY_NS class bar : public xpp::event::sink { public: - explicit bar(connection& conn, const config& config, const logger& logger, - unique_ptr fontmanager, unique_ptr traymanager) + explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr fontmanager, + unique_ptr tray_manager) : m_connection(conn) , m_conf(config) , m_log(logger) , m_fontmanager(forward(fontmanager)) - , m_traymanager(forward(traymanager)) {} + , m_tray(forward(tray_manager)) {} ~bar(); @@ -35,20 +35,29 @@ class bar : public xpp::event::sink m_fontmanager; - unique_ptr m_traymanager; + unique_ptr m_tray; threading_util::spin_lock m_lock; throttle_util::throttle_t m_throttler; @@ -87,14 +96,16 @@ class bar : public xpp::event::sink m_borders; map m_gcontexts; vector m_actions; stateflag m_sinkattached{false}; + alignment m_traypos{alignment::NONE}; + uint16_t m_trayclients{0}; + string m_prevdata; int m_xpos{0}; int m_attributes{0}; @@ -115,7 +126,7 @@ namespace { configure_config(), configure_logger(), configure_fontmanager(), - configure_traymanager()); + configure_tray_manager()); // clang-format on } } diff --git a/include/components/config.hpp b/include/components/config.hpp index 91e33f8c..eeaf4dc3 100644 --- a/include/components/config.hpp +++ b/include/components/config.hpp @@ -70,7 +70,7 @@ class config { * Get list of values for the current bar by name */ template - T get_list(string key) const { + vector get_list(string key) const { return get_list(bar_section(), key); } diff --git a/include/components/controller.hpp b/include/components/controller.hpp index a1ce69f6..70643ebd 100644 --- a/include/components/controller.hpp +++ b/include/components/controller.hpp @@ -16,9 +16,8 @@ LEMONBUDDY_NS class controller { public: - explicit controller(connection& conn, const logger& logger, const config& config, - unique_ptr eventloop, unique_ptr bar, - inotify_util::watch_t& confwatch) + explicit controller(connection& conn, const logger& logger, const config& config, unique_ptr eventloop, + unique_ptr bar, inotify_util::watch_t& confwatch) : m_connection(conn) , m_log(logger) , m_conf(config) diff --git a/include/components/types.hpp b/include/components/types.hpp index b20316c8..3c042076 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -59,28 +59,6 @@ struct bar_settings { } }; -struct tray_settings { - tray_settings() = default; - tray_settings& operator=(const tray_settings& o) = default; - - alignment align{alignment::NONE}; - int16_t orig_x{0}; - int16_t orig_y{0}; - int16_t configured_x{0}; - int16_t configured_y{0}; - uint16_t configured_w{0}; - uint16_t configured_h{0}; - uint16_t configured_slots{0}; - uint16_t width{0}; - uint16_t height{0}; - uint16_t height_fill{0}; - uint16_t spacing{0}; - uint32_t sibling{0}; - uint32_t background{0}; - xcb_pixmap_t back_pixmap{0}; - bool transparent{false}; -}; - struct border_settings { border_settings() = default; lemonbuddy::color color{g_colorblack}; diff --git a/include/x11/graphics.hpp b/include/x11/graphics.hpp index 888c601d..5e194cd2 100644 --- a/include/x11/graphics.hpp +++ b/include/x11/graphics.hpp @@ -18,7 +18,7 @@ namespace graphics_util { xcb_pixmap_t pixmap{0}; }; - bool create_window(connection& conn, xcb_window_t* win, int16_t x = 0, int16_t y = 0, uint16_t w = 1, uint16_t h = 1); + bool create_window(connection& conn, xcb_window_t* win, int16_t x = 0, int16_t y = 0, uint16_t w = 1, uint16_t h = 1, xcb_window_t root = 0); bool create_pixmap(connection& conn, xcb_drawable_t dst, uint16_t w, uint16_t h, xcb_pixmap_t* pixmap); bool create_pixmap(connection& conn, xcb_drawable_t dst, uint16_t w, uint16_t h, uint8_t d, xcb_pixmap_t* pixmap); bool create_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc); diff --git a/include/x11/tray.hpp b/include/x11/tray.hpp index 99f68f87..d28c3775 100644 --- a/include/x11/tray.hpp +++ b/include/x11/tray.hpp @@ -1,18 +1,12 @@ #pragma once #include -#include #include #include "common.hpp" #include "components/logger.hpp" -#include "components/signals.hpp" #include "components/types.hpp" -#include "utils/memory.hpp" -#include "utils/process.hpp" -#include "x11/connection.hpp" #include "x11/graphics.hpp" -#include "x11/xembed.hpp" #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 @@ -26,13 +20,44 @@ LEMONBUDDY_NS -// class definition : trayclient {{{ +// fwd declarations +class connection; +struct xembed_data; -class trayclient { +using root_pixmap = graphics_util::root_pixmap; + +// class definition : settings {{{ + +struct tray_settings { + tray_settings() = default; + tray_settings& operator=(const tray_settings& o) = default; + + alignment align{alignment::NONE}; + int16_t orig_x{0}; + int16_t orig_y{0}; + int16_t configured_x{0}; + int16_t configured_y{0}; + uint16_t configured_w{0}; + uint16_t configured_h{0}; + uint16_t configured_slots{0}; + uint16_t width{0}; + uint16_t width_max{0}; + uint16_t height{0}; + uint16_t height_fill{0}; + uint16_t spacing{0}; + uint32_t sibling{0}; + uint32_t background{0}; + bool transparent{false}; +}; + +// }}} +// class definition : tray_client {{{ + +class tray_client { public: - explicit trayclient(connection& conn, xcb_window_t win, uint16_t w, uint16_t h); + explicit tray_client(connection& conn, xcb_window_t win, uint16_t w, uint16_t h); - ~trayclient(); + ~tray_client(); bool match(const xcb_window_t& win) const; bool mapped() const; @@ -57,16 +82,17 @@ class trayclient { }; // }}} -// class definition : traymanager {{{ +// class definition : tray_manager {{{ -class traymanager - : public xpp::event::sink { +class tray_manager : public xpp::event::sink { public: - explicit traymanager(connection& conn, const logger& logger); + explicit tray_manager(connection& conn, const logger& logger); - ~traymanager(); + ~tray_manager(); + + const tray_settings settings() const; void bootstrap(tray_settings settings); void activate(); @@ -81,6 +107,8 @@ class traymanager void query_atom(); void create_window(); + void create_bg(bool realloc = false); + void restack_window(); void set_wmhints(); void set_traycolors(); @@ -100,8 +128,8 @@ class traymanager int16_t calculate_client_x(const xcb_window_t& win); int16_t calculate_client_y(); - shared_ptr find_client(const xcb_window_t& win) const; - void remove_client(shared_ptr& client, bool reconfigure = true); + shared_ptr find_client(const xcb_window_t& win) const; + void remove_client(shared_ptr& client, bool reconfigure = true); int mapped_clients() const; void handle(const evt::expose& evt); @@ -119,13 +147,13 @@ class traymanager private: connection& m_connection; const logger& m_log; - vector> m_clients; + vector> m_clients; - tray_settings m_settings; + tray_settings m_opts; xcb_gcontext_t m_gc{0}; xcb_pixmap_t m_pixmap{0}; - graphics_util::root_pixmap m_rootpixmap; + root_pixmap m_rootpixmap; uint16_t m_prevwidth{0}; uint16_t m_prevheight{0}; @@ -151,8 +179,8 @@ namespace { /** * Configure injection module */ - template > - di::injector configure_traymanager() { + template > + di::injector configure_tray_manager() { return di::make_injector(configure_logger(), configure_connection()); } } diff --git a/include/x11/window.hpp b/include/x11/window.hpp index 7f8ddef0..cb5e411c 100644 --- a/include/x11/window.hpp +++ b/include/x11/window.hpp @@ -13,16 +13,14 @@ class window : public xpp::window { explicit window(connection_t& conn) : xpp::window(conn, conn.generate_id()) {} - window create_checked( - int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* params); + window create_checked(int16_t x = 0, int16_t y = 0, uint16_t w = 1, uint16_t h = 1); + window create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p); + window create_checked(uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p); - window create_checked(uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* params); + window reconfigure_geom(uint16_t w, uint16_t h, int16_t x = 0, int16_t y = 0); + window reconfigure_pos(int16_t x, int16_t y); - void refresh() { - xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_FULLY_OBSCURED); - xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_UNOBSCURED); - connection().flush(); - } + void redraw(); }; // struct cw_size { diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 83a00c96..859509dd 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -38,8 +38,8 @@ bar::~bar() { // {{{ g_signals::parser::string_write = nullptr; g_signals::tray::report_slotcount = nullptr; // }}} - if (m_traymanager) { - m_traymanager.reset(); + if (m_tray) { + m_tray.reset(); } if (m_sinkattached) { @@ -55,42 +55,23 @@ bar::~bar() { // {{{ * This is done outside the constructor due to boost::di noexcept */ void bar::bootstrap(bool nodraw) { // {{{ + auto bs = m_conf.bar_section(); + + m_screen = m_connection.screen(); + // limit the amount of allowed input events to 1 per 60ms m_throttler = throttle_util::make_throttler(1, 60ms); - m_screen = m_connection.screen(); - m_visual = m_connection.visual_type(m_screen, 32).get(); - auto monitors = randr_util::get_monitors(m_connection, m_connection.screen()->root); - auto bs = m_conf.bar_section(); + m_opts.separator = string_util::trim(m_conf.get(bs, "separator", ""), '"'); + m_opts.locale = m_conf.get(bs, "locale", ""); - // Look for the defined monitor {{{ + create_monitor(); - if (monitors.empty()) - throw application_error("No monitors found"); - - auto monitor_name = m_conf.get(bs, "monitor", ""); - if (monitor_name.empty()) - monitor_name = monitors[0]->name; - - for (auto&& monitor : monitors) { - if (monitor_name.compare(monitor->name) == 0) { - m_bar.monitor = std::move(monitor); - break; - } - } - - if (m_bar.monitor) - m_log.trace("bar: Found matching monitor %s (%ix%i+%i+%i)", m_bar.monitor->name, - m_bar.monitor->w, m_bar.monitor->h, m_bar.monitor->x, m_bar.monitor->y); - else - throw application_error("Could not find monitor: " + monitor_name); - - // }}} // Set bar colors {{{ - m_bar.background = color::parse(m_conf.get(bs, "background", m_bar.background.source())); - m_bar.foreground = color::parse(m_conf.get(bs, "foreground", m_bar.foreground.source())); - m_bar.linecolor = color::parse(m_conf.get(bs, "linecolor", m_bar.linecolor.source())); + m_opts.background = color::parse(m_conf.get(bs, "background", m_opts.background.source())); + m_opts.foreground = color::parse(m_conf.get(bs, "foreground", m_opts.foreground.source())); + m_opts.linecolor = color::parse(m_conf.get(bs, "linecolor", m_opts.linecolor.source())); // }}} // Set border values {{{ @@ -104,8 +85,7 @@ void bar::bootstrap(bool nodraw) { // {{{ m_borders.emplace(border::BOTTOM, border_settings{}); m_borders[border::BOTTOM].size = m_conf.get(bs, "border-bottom", bsize); - m_borders[border::BOTTOM].color = - color::parse(m_conf.get(bs, "border-bottom-color", bcolor)); + m_borders[border::BOTTOM].color = color::parse(m_conf.get(bs, "border-bottom-color", bcolor)); m_borders.emplace(border::LEFT, border_settings{}); m_borders[border::LEFT].size = m_conf.get(bs, "border-left", bsize); @@ -113,308 +93,59 @@ void bar::bootstrap(bool nodraw) { // {{{ m_borders.emplace(border::RIGHT, border_settings{}); m_borders[border::RIGHT].size = m_conf.get(bs, "border-right", bsize); - m_borders[border::RIGHT].color = - color::parse(m_conf.get(bs, "border-right-color", bcolor)); + m_borders[border::RIGHT].color = color::parse(m_conf.get(bs, "border-right-color", bcolor)); // }}} // Set size and position {{{ - GET_CONFIG_VALUE(bs, m_bar.dock, "dock"); - GET_CONFIG_VALUE(bs, m_bar.bottom, "bottom"); - GET_CONFIG_VALUE(bs, m_bar.spacing, "spacing"); - GET_CONFIG_VALUE(bs, m_bar.lineheight, "lineheight"); - GET_CONFIG_VALUE(bs, m_bar.padding_left, "padding-left"); - GET_CONFIG_VALUE(bs, m_bar.padding_right, "padding-right"); - GET_CONFIG_VALUE(bs, m_bar.module_margin_left, "module-margin-left"); - GET_CONFIG_VALUE(bs, m_bar.module_margin_right, "module-margin-right"); - - auto w = m_conf.get(bs, "width", "100%"); - auto h = m_conf.get(bs, "height", "24"); - - auto offsetx = m_conf.get(bs, "offset-x", ""); - auto offsety = m_conf.get(bs, "offset-y", ""); - - // look for user-defined width - if ((m_bar.width = std::atoi(w.c_str())) && w.find("%") != string::npos) { - m_bar.width = math_util::percentage_to_value(m_bar.width, m_bar.monitor->w); - } - - // look for user-defined height - if ((m_bar.height = std::atoi(h.c_str())) && h.find("%") != string::npos) { - m_bar.height = math_util::percentage_to_value(m_bar.height, m_bar.monitor->h); - } - - // look for user-defined offset-x - if ((m_bar.offset_x = std::atoi(offsetx.c_str())) != 0 && offsetx.find("%") != string::npos) { - m_bar.offset_x = math_util::percentage_to_value(m_bar.offset_x, m_bar.monitor->w); - } - - // look for user-defined offset-y - if ((m_bar.offset_y = std::atoi(offsety.c_str())) != 0 && offsety.find("%") != string::npos) { - m_bar.offset_y = math_util::percentage_to_value(m_bar.offset_y, m_bar.monitor->h); - } - - // apply offsets - m_bar.x = m_bar.offset_x + m_bar.monitor->x; - m_bar.y = m_bar.offset_y + m_bar.monitor->y; - - // apply borders - m_bar.height += m_borders[border::TOP].size; - m_bar.height += m_borders[border::BOTTOM].size; - - if (m_bar.bottom) - m_bar.y = m_bar.monitor->y + m_bar.monitor->h - m_bar.height - m_bar.offset_y; - - if (m_bar.width <= 0 || m_bar.width > m_bar.monitor->w) - throw application_error("Resulting bar width is out of bounds"); - if (m_bar.height <= 0 || m_bar.height > m_bar.monitor->h) - throw application_error("Resulting bar height is out of bounds"); - - m_bar.width = math_util::cap(m_bar.width, 0, m_bar.monitor->w); - m_bar.height = math_util::cap(m_bar.height, 0, m_bar.monitor->h); - - m_bar.vertical_mid = - (m_bar.height + m_borders[border::TOP].size - m_borders[border::BOTTOM].size) / 2; - - m_log.trace("bar: Resulting bar geom %ix%i+%i+%i", m_bar.width, m_bar.height, m_bar.x, m_bar.y); + GET_CONFIG_VALUE(bs, m_opts.dock, "dock"); + GET_CONFIG_VALUE(bs, m_opts.bottom, "bottom"); + GET_CONFIG_VALUE(bs, m_opts.spacing, "spacing"); + GET_CONFIG_VALUE(bs, m_opts.lineheight, "lineheight"); + GET_CONFIG_VALUE(bs, m_opts.padding_left, "padding-left"); + GET_CONFIG_VALUE(bs, m_opts.padding_right, "padding-right"); + GET_CONFIG_VALUE(bs, m_opts.module_margin_left, "module-margin-left"); + GET_CONFIG_VALUE(bs, m_opts.module_margin_right, "module-margin-right"); // }}} // Set the WM_NAME value {{{ + // Required early for --print-wmname - m_bar.wmname = "lemonbuddy-" + bs.substr(4) + "_" + m_bar.monitor->name; - m_bar.wmname = m_conf.get(bs, "wm-name", m_bar.wmname); - m_bar.wmname = string_util::replace(m_bar.wmname, " ", "-"); + m_opts.wmname = "lemonbuddy-" + bs.substr(4) + "_" + m_opts.monitor->name; + m_opts.wmname = m_conf.get(bs, "wm-name", m_opts.wmname); + m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-"); // }}} - // Set misc parameters {{{ - - m_bar.separator = string_util::trim(m_conf.get(bs, "separator", ""), '"'); - m_bar.locale = m_conf.get(bs, "locale", ""); - - // }}} - // Checking nodraw {{{ + // Check nodraw flag {{{ if (nodraw) { m_log.trace("bar: Abort bootstrap routine (reason: nodraw)"); - m_traymanager.reset(); + m_tray.reset(); return; } // }}} - // Setup graphic components and create the window {{{ + // Setup graphic components and create window {{{ + + m_log.trace("bar: Get true color visual"); + m_visual = m_connection.visual_type(m_screen, 32).get(); m_log.trace("bar: Create colormap"); - { - m_connection.create_colormap( - XCB_COLORMAP_ALLOC_NONE, m_colormap, m_screen->root, m_visual->visual_id); - } + m_colormap = colormap{m_connection, m_connection.generate_id()}; + m_connection.create_colormap(XCB_COLORMAP_ALLOC_NONE, m_colormap, m_screen->root, m_visual->visual_id); - m_log.trace("bar: Create window %s", m_connection.id(m_window)); - { - uint32_t mask = 0; - xcb_params_cw_t params; - // clang-format off - XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixel, 0); - XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, 0); - XCB_AUX_ADD_PARAM(&mask, ¶ms, backing_store, XCB_BACKING_STORE_WHEN_MAPPED); - XCB_AUX_ADD_PARAM(&mask, ¶ms, colormap, m_colormap); - XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, m_bar.dock); - XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS); - // clang-format on - m_window.create_checked(m_bar.x, m_bar.y, m_bar.width, m_bar.height, mask, ¶ms); - } + configure_geom(); + create_window(); + create_pixmap(); + create_gcontexts(); + create_rootpixmap(); + restack_window(); + set_wmhints(); - m_log.trace("bar: Set WM_NAME"); - xcb_icccm_set_wm_name( - m_connection, m_window, XCB_ATOM_STRING, 8, m_bar.wmname.length(), m_bar.wmname.c_str()); - xcb_icccm_set_wm_class(m_connection, m_window, 21, "lemonbuddy\0Lemonbuddy"); + // m_log.trace("bar: Listen to X RandR events"); + // m_connection.select_input(m_window, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE); - m_log.trace("bar: Set _NET_WM_WINDOW_TYPE"); - wm_util::set_windowtype(m_connection, m_window, {_NET_WM_WINDOW_TYPE_DOCK}); - - m_log.trace("bar: Set _NET_WM_STATE"); - wm_util::set_wmstate(m_connection, m_window, {_NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE}); - - m_log.trace("bar: Set _NET_WM_DESKTOP"); - wm_util::set_wmdesktop(m_connection, m_window, -1u); - - m_log.trace("bar: Set _NET_WM_PID"); - wm_util::set_wmpid(m_connection, m_window, getpid()); - - m_log.trace("bar: Set _NET_WM_STRUT_PARTIAL"); - { - uint32_t none{0}; - uint32_t value_list[12]{none}; - - auto mt = m_conf.get("global/wm", "margin-top", 0); - auto mb = m_conf.get("global/wm", "margin-bottom", 0); - auto ml = m_conf.get("global/wm", "margin-left", 0); - auto mr = m_conf.get("global/wm", "margin-right", 0); - - if (m_bar.bottom) { - value_list[3] = m_bar.height + mt; - value_list[10] = m_bar.x + ml; - value_list[11] = m_bar.x + m_bar.width + mr; - } else { - value_list[2] = m_bar.height + mb; - value_list[8] = m_bar.x + ml; - value_list[9] = m_bar.x + m_bar.width + mr; - } - - m_connection.change_property(XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STRUT_PARTIAL, - XCB_ATOM_CARDINAL, 32, 12, value_list); - } - - m_log.trace("bar: Create pixmap"); - { - m_connection.create_pixmap( - m_visual->visual_id == m_screen->root_visual ? XCB_COPY_FROM_PARENT : 32, m_pixmap, - m_window, m_bar.width, m_bar.height); - } - - m_log.trace("bar: Map window"); - { - m_connection.map_window(m_window); - m_connection.flush(); - } - - // }}} - // Restack window and put it above defined WM's root {{{ - - try { - auto wm_restack = m_conf.get(bs, "wm-restack"); - auto restacked = false; - - if (wm_restack == "bspwm") { - restacked = bspwm_util::restack_above_root(m_connection, m_bar.monitor, m_window); - - } else if (wm_restack == "i3" && m_bar.dock && ENABLE_I3) { -#if ENABLE_I3 - restacked = i3_util::restack_above_root(m_connection, m_bar.monitor, m_window); -#endif - - } else if (wm_restack == "i3" && !m_bar.dock) { - m_log.warn("Ignoring restack of i3 window (not needed when dock = false)"); - wm_restack.clear(); - - } else { - m_log.warn("Ignoring unsupported wm-restack option '%s'", wm_restack); - wm_restack.clear(); - } - - if (restacked) { - m_log.info("Successfully restacked bar window"); - } else if (!wm_restack.empty()) { - m_log.err("Failed to restack bar window"); - } - } catch (const key_error& err) { - } - - // }}} - // Create graphic contexts {{{ - - m_log.trace("bar: Create graphic contexts"); - { - // clang-format off - vector colors { - m_bar.background, - m_bar.foreground, - m_bar.linecolor, - m_bar.linecolor, - m_borders[border::TOP].color, - m_borders[border::BOTTOM].color, - m_borders[border::LEFT].color, - m_borders[border::RIGHT].color, - }; - // clang-format on - - for (int i = 1; i <= 8; i++) { - uint32_t mask = 0; - uint32_t value_list[32]; - xcb_params_gc_t params; - m_gcontexts.emplace(gc(i), gcontext{m_connection, m_connection.generate_id()}); - - XCB_AUX_ADD_PARAM(&mask, ¶ms, foreground, colors[i - 1]); - XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 0); - - xutils::pack_values(mask, ¶ms, value_list); - - m_connection.create_gc(m_gcontexts.at(gc(i)), m_pixmap, mask, value_list); - } - } - - // }}} - // Setup root pixmap {{{ - - m_log.trace("bar: Setup root pixmap"); - { - // graphics_util::get_root_pixmap(m_connection, &m_rootpixmap); - // graphics_util::simple_gc(m_connection, m_pixmap, &m_root_gc); - // - // if (!m_rootpixmap.pixmap || !m_pixmap || !m_root_gc) { - // m_log.warn("Failed to get root pixmap for bar window background"); - // } else { - // m_log.trace("bar: rootpixmap=%x (%dx%d+%d+%d)", m_rootpixmap.pixmap, m_rootpixmap.width, - // m_rootpixmap.height, m_rootpixmap.x, m_rootpixmap.y); - // - // m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_root_gc, m_bar.x, m_bar.y, 0, 0, - // m_bar.width, m_bar.height); - // - // auto image_reply = m_connection.get_image( - // XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, 0, 0, m_bar.width, m_bar.height, XAllPlanes()); - // - // std::vector image_data; - // std::back_insert_iterator back_it(image_data); - // std::copy(image_reply.data().begin(), image_reply.data().end(), back_it); - // - // m_connection.put_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_root_gc, m_bar.width, - // m_bar.height, 0, 0, 0, image_reply->depth, image_data.size(), image_data.data()); - // - // m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_root_gc, m_bar.x, m_bar.y, 0, 0, - // m_bar.width, m_bar.height); - // - // uint32_t mask = 0; - // uint32_t values[16]; - // xcb_params_cw_t params; - // XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, m_pixmap); - // xutils::pack_values(mask, ¶ms, values); - // m_connection.change_window_attributes_checked(m_window, mask, values); - // - // m_connection.copy_area( - // m_pixmap, m_window, m_root_gc, m_bar.x, m_bar.y, 0, 0, m_bar.width, m_bar.height); - // } - } - - // }}} - // Load fonts {{{ - - auto fonts_loaded = false; - auto fontindex = 0; - auto fonts = m_conf.get_list(bs, "font"); - - for (auto f : fonts) { - fontindex++; - vector fd = string_util::split(f, ';'); - string pattern{fd[0]}; - int offset{0}; - - if (fd.size() > 1) - offset = std::stoi(fd[1], 0, 10); - - if (m_fontmanager->load(pattern, fontindex, offset)) - fonts_loaded = true; - else - m_log.warn("Unable to load font '%s'", fd[0]); - } - - if (!fonts_loaded) { - m_log.warn("Loading fallback font"); - - if (!m_fontmanager->load("fixed")) - throw application_error("Unable to load fonts"); - } - - m_fontmanager->allocate_color(m_bar.foreground, true); + m_connection.map_window(m_window); // }}} // Connect signal handlers {{{ @@ -437,15 +168,12 @@ void bar::bootstrap(bool nodraw) { // {{{ // clang-format on // }}} - // Attach event sink to registry {{{ - m_log.trace("bar: Aattaching sink to registry"); + m_log.trace("bar: Attaching sink to registry"); m_connection.attach_sink(this, 1); m_sinkattached = true; - // }}} - - bootstrap_tray(); + load_fonts(); m_connection.flush(); } // }}} @@ -454,101 +182,106 @@ void bar::bootstrap(bool nodraw) { // {{{ * Setup tray manager */ void bar::bootstrap_tray() { // {{{ - auto bs = m_conf.bar_section(); - - try { - auto tray_position = m_conf.get(bs, "tray-position"); - - if (tray_position == "left") - 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) { - m_tray.align = alignment::NONE; - } - - if (m_tray.align == alignment::NONE) { - m_log.warn("Disabling tray manager (reason: disabled in config)"); - m_traymanager.reset(); + if (!m_tray) { return; } - 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; + tray_settings settings; - if (m_tray.height % 2 != 0) { - m_tray.height--; + auto bs = m_conf.bar_section(); + auto tray_position = m_conf.get(bs, "tray-position"); + + if (tray_position == "left") + settings.align = alignment::LEFT; + else if (tray_position == "right") + settings.align = alignment::RIGHT; + else if (tray_position == "center") + settings.align = alignment::CENTER; + else + settings.align = alignment::NONE; + + if (settings.align == alignment::NONE) { + m_log.warn("Disabling tray manager (reason: disabled in config)"); + m_tray.reset(); + return; + } + + m_traypos = settings.align; + + settings.height = m_opts.height; + settings.height -= m_borders.at(border::BOTTOM).size; + settings.height -= m_borders.at(border::TOP).size; + settings.height_fill = settings.height; + + if (settings.height % 2 != 0) { + settings.height--; } 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; + if (settings.height > maxsize) { + settings.spacing += (settings.height - maxsize) / 2; + settings.height = maxsize; } - m_tray.width = m_tray.height; - m_tray.orig_y = m_bar.y + m_borders.at(border::TOP).size; + settings.width_max = m_opts.width; + settings.width = settings.height; + settings.orig_y = m_opts.y + m_borders.at(border::TOP).size; // Apply user-defined scaling auto scale = m_conf.get(bs, "tray-scale", 1.0); - m_tray.width *= scale; - m_tray.height_fill *= scale; + settings.width *= scale; + settings.height_fill *= scale; - 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); + if (settings.align == alignment::RIGHT) { + settings.orig_x = m_opts.x + m_opts.width - m_borders.at(border::RIGHT).size; + } else if (settings.align == alignment::LEFT) { + settings.orig_x = m_opts.x + m_borders.at(border::LEFT).size; + } else if (settings.align == alignment::CENTER) { + settings.orig_x = get_centerx() - (settings.width / 2); } // Set user-defined background color - m_conf.get(bs, "tray-transparent", m_tray.transparent); - - if (m_tray.transparent) { - m_tray.background = 0; - } else { + if (!(settings.transparent = m_conf.get(bs, "tray-transparent", settings.transparent))) { auto bg = m_conf.get(bs, "tray-background", ""); + + if (bg.length() > 7) { + m_log.warn("Alpha support for the systray is limited. See the wiki for more details."); + } + if (!bg.empty()) { - m_tray.background = color::parse(bg, g_colorempty); + settings.background = color::parse(bg, g_colorempty); } else { - m_tray.background = m_bar.background; + settings.background = m_opts.background; + } + + if (color_util::alpha_channel(settings.background) == 0) { + settings.transparent = true; + settings.background = 0; } } - if (color_util::alpha_channel(m_tray.background) == 0) { - m_tray.transparent = true; - m_tray.background = 0; - } - // Add user-defined padding - m_tray.spacing += m_conf.get(bs, "tray-padding", 0); + settings.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()); + auto offset_x = atoi(offset_x_def.c_str()); + auto offset_y = 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; + offset_x = math_util::percentage_to_value(offset_x, m_opts.monitor->w); + offset_x -= settings.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; + offset_y = math_util::percentage_to_value(offset_y, m_opts.monitor->h); + offset_y -= settings.width / 2; } - m_tray.orig_x += offset_x; - m_tray.orig_y += offset_y; + settings.orig_x += offset_x; + settings.orig_y += offset_y; // Add tray update callback unless explicitly disabled if (!m_conf.get(bs, "tray-detached", false)) { @@ -556,15 +289,15 @@ void bar::bootstrap_tray() { // {{{ } // Put the tray next to the bar in the window stack - m_tray.sibling = m_window; + settings.sibling = m_window; try { - m_log.trace("controller: Setup tray manager"); - m_traymanager->bootstrap(tray()); - } catch (const std::exception& err) { + m_log.trace("bar: Setup tray manager"); + m_tray->bootstrap(settings); + } catch (const exception& err) { m_log.err(err.what()); m_log.warn("Failed to setup tray, disabling..."); - m_traymanager.reset(); + m_tray.reset(); } } // }}} @@ -572,18 +305,18 @@ void bar::bootstrap_tray() { // {{{ * Activate tray manager */ void bar::activate_tray() { // {{{ - if (!m_traymanager) { + if (!m_tray) { return; } - m_log.trace("controller: Activate tray manager"); + m_log.trace("bar: Activate tray manager"); try { - m_traymanager->activate(); - } catch (const std::exception& err) { + m_tray->activate(); + } catch (const exception& err) { m_log.err(err.what()); m_log.err("Failed to activate tray manager, disabling..."); - m_traymanager.reset(); + m_tray.reset(); } } // }}} @@ -591,14 +324,7 @@ void bar::activate_tray() { // {{{ * Get the bar settings container */ const bar_settings bar::settings() const { // {{{ - return m_bar; -} // }}} - -/** - * Get the tray settings container - */ -const tray_settings bar::tray() const { // {{{{ - return m_tray; + return m_opts; } // }}} /** @@ -618,7 +344,7 @@ void bar::parse(string data, bool force) { // {{{ // TODO: move to fontmanager m_xftdraw = XftDrawCreate(xlib::get_display(), m_pixmap, xlib::get_visual(), m_colormap); - m_bar.align = alignment::LEFT; + m_opts.align = alignment::LEFT; m_xpos = m_borders[border::LEFT].size; m_attributes = 0; @@ -632,25 +358,28 @@ void bar::parse(string data, bool force) { // {{{ draw_background(); - if (m_tray.align == alignment::LEFT && m_tray.configured_slots) - m_xpos += ((m_tray.width + m_tray.spacing) * m_tray.configured_slots) + m_tray.spacing; + if (m_traypos == alignment::LEFT && m_trayclients > 0) { + auto& tray = m_tray->settings(); + m_xpos += ((tray.width + tray.spacing) * m_trayclients) + tray.spacing; + } try { - parser parser(m_bar); + parser parser(m_opts); parser(data); } catch (const unrecognized_token& err) { m_log.err("Unrecognized syntax token '%s'", err.what()); } - if (m_tray.align == alignment::RIGHT && m_tray.configured_slots) - draw_shift( - m_xpos, ((m_tray.width + m_tray.spacing) * m_tray.configured_slots) + m_tray.spacing); + if (m_traypos == alignment::RIGHT && m_trayclients > 0) { + auto& tray = m_tray->settings(); + draw_shift(m_xpos, ((tray.width + tray.spacing) * m_trayclients) + tray.spacing); + } draw_border(border::ALL); - flush(); - XftDrawDestroy(m_xftdraw); + + flush(); } } // }}} @@ -658,12 +387,7 @@ void bar::parse(string data, bool force) { // {{{ * Copy the contents of the pixmap's onto the bar window */ void bar::flush() { // {{{ - m_connection.copy_area( - m_pixmap, m_window, m_gcontexts.at(gc::FG), 0, 0, 0, 0, m_bar.width, m_bar.height); - - // m_connection.copy_area( - // m_pixmap, m_window, m_root_gc, m_bar.x, m_bar.y, 0, 0, m_bar.width, m_bar.height); - + m_connection.copy_area(m_pixmap, m_window, m_gcontexts.at(gc::FG), 0, 0, 0, 0, m_opts.width, m_opts.height); m_connection.flush(); if (g_signals::bar::redraw) { @@ -689,19 +413,17 @@ void bar::flush() { // {{{ hint_num[action.align]++; auto x = action.start_x; - auto y = m_bar.y + hint_num[action.align]++ * DRAW_CLICKABLE_AREA_HINTS_OFFSET_Y; + auto y = m_opts.y + hint_num[action.align]++ * DRAW_CLICKABLE_AREA_HINTS_OFFSET_Y; auto w = action.end_x - action.start_x - 2; - auto h = m_bar.height - 2; + auto h = m_opts.height - 2; const uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT; const uint32_t border_color = hint_num[action.align] % 2 ? 0xff0000 : 0x00ff00; const uint32_t values[2]{border_color, true}; - auto scr = m_connection.screen(); - - action.clickable_area = m_connection.generate_id(); - m_connection.create_window_checked(scr->root_depth, action.clickable_area, scr->root, x, y, w, - h, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, mask, values); + action.clickable_area = window{m_connection, m_connection.generate_id()}; + m_connection.create_window_checked(m_screen->root_depth, action.clickable_area, m_screen->root, x, y, w, h, 1, + XCB_WINDOW_CLASS_INPUT_OUTPUT, m_screen->root_visual, mask, values); m_connection.map_window_checked(action.clickable_area); #endif } @@ -715,6 +437,345 @@ void bar::refresh_window() { // {{{ m_log.info("Refresh bar window"); } // }}} +/** + * Load user-defined fonts + */ +void bar::load_fonts() { // {{{ + auto fonts_loaded = false; + auto fontindex = 0; + auto fonts = m_conf.get_list("font"); + + for (auto f : fonts) { + fontindex++; + vector fd = string_util::split(f, ';'); + string pattern{fd[0]}; + int offset{0}; + + if (fd.size() > 1) + offset = std::stoi(fd[1], 0, 10); + + if (m_fontmanager->load(pattern, fontindex, offset)) + fonts_loaded = true; + else + m_log.warn("Unable to load font '%s'", fd[0]); + } + + if (!fonts_loaded) { + m_log.warn("Loading fallback font"); + + if (!m_fontmanager->load("fixed")) + throw application_error("Unable to load fonts"); + } + + m_fontmanager->allocate_color(m_opts.foreground, true); +} // }}} + +/** + * Configure geometry values + */ +void bar::configure_geom() { // {{{ + m_log.trace("bar: Configure window geometry"); + + auto w = m_conf.get(m_conf.bar_section(), "width", "100%"); + auto h = m_conf.get(m_conf.bar_section(), "height", "24"); + + auto offsetx = m_conf.get(m_conf.bar_section(), "offset-x", ""); + auto offsety = m_conf.get(m_conf.bar_section(), "offset-y", ""); + + // look for user-defined width + if ((m_opts.width = atoi(w.c_str())) && w.find("%") != string::npos) { + m_opts.width = math_util::percentage_to_value(m_opts.width, m_opts.monitor->w); + } + + // look for user-defined height + if ((m_opts.height = atoi(h.c_str())) && h.find("%") != string::npos) { + m_opts.height = math_util::percentage_to_value(m_opts.height, m_opts.monitor->h); + } + + // look for user-defined offset-x + if ((m_opts.offset_x = atoi(offsetx.c_str())) != 0 && offsetx.find("%") != string::npos) { + m_opts.offset_x = math_util::percentage_to_value(m_opts.offset_x, m_opts.monitor->w); + } + + // look for user-defined offset-y + if ((m_opts.offset_y = atoi(offsety.c_str())) != 0 && offsety.find("%") != string::npos) { + m_opts.offset_y = math_util::percentage_to_value(m_opts.offset_y, m_opts.monitor->h); + } + + // apply offsets + m_opts.x = m_opts.offset_x + m_opts.monitor->x; + m_opts.y = m_opts.offset_y + m_opts.monitor->y; + + // apply borders + m_opts.height += m_borders[border::TOP].size; + m_opts.height += m_borders[border::BOTTOM].size; + + if (m_opts.bottom) + m_opts.y = m_opts.monitor->y + m_opts.monitor->h - m_opts.height - m_opts.offset_y; + + if (m_opts.width <= 0 || m_opts.width > m_opts.monitor->w) + throw application_error("Resulting bar width is out of bounds"); + if (m_opts.height <= 0 || m_opts.height > m_opts.monitor->h) + throw application_error("Resulting bar height is out of bounds"); + + m_opts.width = math_util::cap(m_opts.width, 0, m_opts.monitor->w); + m_opts.height = math_util::cap(m_opts.height, 0, m_opts.monitor->h); + + m_opts.vertical_mid = (m_opts.height + m_borders[border::TOP].size - m_borders[border::BOTTOM].size) / 2; + + m_log.info("Bar geometry %ix%i+%i+%i", m_opts.width, m_opts.height, m_opts.x, m_opts.y); +} // }}} + +/** + * Create monitor object + */ +void bar::create_monitor() { // {{{ + m_log.trace("bar: Create monitor from matching X RandR output"); + + auto monitors = randr_util::get_monitors(m_connection, m_screen->root); + if (monitors.empty()) { + throw application_error("No monitors found"); + } + + auto name = m_conf.get(m_conf.bar_section(), "monitor", ""); + if (name.empty()) { + name = monitors[0]->name; + m_log.warn("No monitor specified, using \"%s\"", name); + } + + for (auto&& monitor : monitors) { + if (name == monitor->name) { + m_opts.monitor = move(monitor); + break; + } + } + + if (!m_opts.monitor) { + throw application_error("Monitor \"" + name + "\" not found or disconnected"); + } + + const auto& m = m_opts.monitor; + m_log.trace("bar: Loaded monitor %s (%ix%i+%i+%i)", m->name, m->w, m->h, m->x, m->y); +} // }}} + +/** + * Create window object + */ +void bar::create_window() { // {{{ + m_log.trace("bar: Create window %s", m_connection.id(m_window)); + + uint32_t mask = 0; + xcb_params_cw_t params; + + // clang-format off + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixel, 0); + XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, 0); + XCB_AUX_ADD_PARAM(&mask, ¶ms, backing_store, XCB_BACKING_STORE_WHEN_MAPPED); + XCB_AUX_ADD_PARAM(&mask, ¶ms, colormap, m_colormap); + XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, m_opts.dock); + XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS); + // clang-format on + + uint32_t values[16]; + xutils::pack_values(mask, ¶ms, values); + + m_connection.create_window_checked(32, m_window, m_screen->root, m_opts.x, m_opts.y, m_opts.width, m_opts.height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, m_visual->visual_id, mask, values); +} // }}} + +/** + * Create window pixmap + */ +void bar::create_pixmap() { // {{{ + m_log.trace("bar: Create pixmap (xid=%s)", m_connection.id(m_pixmap)); + m_connection.create_pixmap(32, m_pixmap, m_window, m_opts.width, m_opts.height); +} // }}} + +/** + * Create window gcontexts + */ +void bar::create_gcontexts() { // {{{ + // clang-format off + vector colors { + m_opts.background, + m_opts.foreground, + m_opts.linecolor, + m_opts.linecolor, + m_borders[border::TOP].color, + m_borders[border::BOTTOM].color, + m_borders[border::LEFT].color, + m_borders[border::RIGHT].color, + }; + // clang-format on + + for (int i = 1; i <= 8; i++) { + uint32_t mask = 0; + uint32_t value_list[32]; + + xcb_params_gc_t params; + XCB_AUX_ADD_PARAM(&mask, ¶ms, foreground, colors[i - 1]); + XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 0); + + xutils::pack_values(mask, ¶ms, value_list); + m_gcontexts.emplace(gc(i), gcontext{m_connection, m_connection.generate_id()}); + + m_log.trace("bar: Create gcontext (gc=%i, xid=%s)", i, m_connection.id(m_gcontexts.at(gc(i)))); + m_connection.create_gc(m_gcontexts.at(gc(i)), m_pixmap, mask, value_list); + } +} // }}} + +/** + * Create an X image from the root pixmap + * used to render pseudo-transparent backgrounds + */ +void bar::create_rootpixmap() { // {{{ +#if 0 + m_log.trace("bar: Create root pixmap"); + graphics_util::get_root_pixmap(m_connection, &m_rootpixmap); + graphics_util::simple_gc(m_connection, m_pixmap, &m_root_gc); + + if (!m_rootpixmap.pixmap || !m_pixmap || !m_root_gc) { + m_log.warn("Failed to get root pixmap for bar window background"); + } else { + m_log.trace("bar: rootpixmap=%x (%dx%d+%d+%d)", m_rootpixmap.pixmap, m_rootpixmap.width, + m_rootpixmap.height, m_rootpixmap.x, m_rootpixmap.y); + + m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_root_gc, m_opts.x, + m_opts.y, 0, 0, + m_opts.width, m_opts.height); + + auto image_reply = m_connection.get_image( + XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, 0, 0, m_opts.width, m_opts.height, + XAllPlanes()); + + vector image_data; + std::back_insert_iterator back_it(image_data); + std::copy(image_reply.data().begin(), image_reply.data().end(), back_it); + + m_connection.put_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_root_gc, m_opts.width, + m_opts.height, 0, 0, 0, image_reply->depth, image_data.size(), image_data.data()); + + m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_root_gc, m_opts.x, + m_opts.y, 0, 0, + m_opts.width, m_opts.height); + + uint32_t mask = 0; + uint32_t values[16]; + xcb_params_cw_t params; + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, m_pixmap); + xutils::pack_values(mask, ¶ms, values); + m_connection.change_window_attributes_checked(m_window, mask, values); + + m_connection.copy_area( + m_pixmap, m_window, m_root_gc, m_opts.x, m_opts.y, 0, 0, m_opts.width, + m_opts.height); + } +#endif +} // }}} + +/** + * Move the bar window above defined sibling + * in the X window stack + */ +void bar::restack_window() { // {{{ + string wm_restack; + + try { + wm_restack = m_conf.get(m_conf.bar_section(), "wm-restack"); + } catch (const key_error& err) { + return; + } + + auto restacked = false; + + if (wm_restack == "bspwm") { + restacked = bspwm_util::restack_above_root(m_connection, m_opts.monitor, m_window); +#if ENABLE_I3 + } else if (wm_restack == "i3" && m_opts.dock) { + restacked = i3_util::restack_above_root(m_connection, m_opts.monitor, m_window); + } else if (wm_restack == "i3" && !m_opts.dock) { + m_log.warn("Ignoring restack of i3 window (not needed when dock = false)"); + wm_restack.clear(); +#endif + } else { + m_log.warn("Ignoring unsupported wm-restack option '%s'", wm_restack); + wm_restack.clear(); + } + + if (restacked) { + m_log.info("Successfully restacked bar window"); + } else if (!wm_restack.empty()) { + m_log.err("Failed to restack bar window"); + } +} // }}} + +/** + * Set window atom values + */ +void bar::set_wmhints() { // {{{ + m_log.trace("bar: Set WM_NAME"); + xcb_icccm_set_wm_name(m_connection, m_window, XCB_ATOM_STRING, 8, m_opts.wmname.length(), m_opts.wmname.c_str()); + xcb_icccm_set_wm_class(m_connection, m_window, 21, "lemonbuddy\0Lemonbuddy"); + + m_log.trace("bar: Set _NET_WM_WINDOW_TYPE"); + wm_util::set_windowtype(m_connection, m_window, {_NET_WM_WINDOW_TYPE_DOCK}); + + m_log.trace("bar: Set _NET_WM_STATE"); + wm_util::set_wmstate(m_connection, m_window, {_NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE}); + + m_log.trace("bar: Set _NET_WM_DESKTOP"); + wm_util::set_wmdesktop(m_connection, m_window, -1u); + + m_log.trace("bar: Set _NET_WM_PID"); + wm_util::set_wmpid(m_connection, m_window, getpid()); + + m_log.trace("bar: Set _NET_WM_STRUT_PARTIAL"); + { + uint32_t none{0}; + uint32_t value_list[12]{none}; + + auto mt = m_conf.get("global/wm", "margin-top", 0); + auto mb = m_conf.get("global/wm", "margin-bottom", 0); + auto ml = m_conf.get("global/wm", "margin-left", 0); + auto mr = m_conf.get("global/wm", "margin-right", 0); + + if (m_opts.bottom) { + value_list[3] = m_opts.monitor->y + m_opts.height + mt; + value_list[10] = m_opts.monitor->x + m_opts.x + ml; + value_list[11] = m_opts.monitor->x + m_opts.x + m_opts.width + mr; + } else { + value_list[2] = m_opts.monitor->y + m_opts.height + mb; + value_list[8] = m_opts.monitor->x + m_opts.x + ml; + value_list[9] = m_opts.monitor->x + m_opts.x + m_opts.width + mr; + } + + m_connection.change_property( + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, 12, value_list); + } +} // }}} + +/** + * Get the horizontal center pos + */ +int bar::get_centerx() { // {{{ + int x = m_opts.x; + x += m_opts.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 bar::get_innerwidth() { // {{{ + auto w = m_opts.width; + w -= m_borders[border::RIGHT].size; + w -= m_borders[border::LEFT].size; + return w; +} // }}} + /** * Event handler for XCB_BUTTON_PRESS events * @@ -727,8 +788,8 @@ void bar::handle(const evt::button_press& evt) { // {{{ std::lock_guard lck(m_lock); { - m_log.trace_x("bar: Received button press event: %i at pos(%i, %i)", - static_cast(evt->detail), evt->event_x, evt->event_y); + m_log.trace_x("bar: Received button press event: %i at pos(%i, %i)", static_cast(evt->detail), evt->event_x, + evt->event_y); mousebtn button = static_cast(evt->detail); @@ -740,8 +801,7 @@ void bar::handle(const evt::button_press& evt) { // {{{ m_log.trace_x("bar: Ignoring action: button mismatch"); continue; } else if (action.start_x > evt->event_x) { - m_log.trace_x( - "bar: Ignoring action: start_x(%i) > event_x(%i)", action.start_x, evt->event_x); + m_log.trace_x("bar: Ignoring action: start_x(%i) > event_x(%i)", action.start_x, evt->event_x); continue; } else if (action.end_x < evt->event_x) { m_log.trace_x("bar: Ignoring action: end_x(%i) < event_x(%i)", action.end_x, evt->event_x); @@ -772,10 +832,10 @@ void bar::handle(const evt::button_press& evt) { // {{{ * Used to redraw the bar */ void bar::handle(const evt::expose& evt) { // {{{ - if (evt->window != m_window) - return; - m_log.trace("bar: Received expose event"); - flush(); + if (evt->window == m_window) { + m_log.trace("bar: Received expose event"); + flush(); + } } // }}} /** @@ -795,7 +855,10 @@ void bar::handle(const evt::expose& evt) { // {{{ * Also tracks the root pixmap */ void bar::handle(const evt::property_notify& evt) { // {{{ - m_log.trace("bar: property_notify"); +#ifdef DEBUG + string atom_name = m_connection.get_atom_name(evt->atom).name(); + m_log.trace("bar: property_notify(%s)", atom_name); +#endif if (evt->window == m_window && evt->atom == WM_STATE) { if (!g_signals::bar::visibility_change) { @@ -812,7 +875,7 @@ void bar::handle(const evt::property_notify& evt) { // {{{ g_signals::bar::visibility_change(false); else g_signals::bar::visibility_change(true); - } catch (const std::exception& err) { + } catch (const exception& err) { m_log.warn("Failed to emit bar window's visibility change event"); } } else if (evt->atom == _XROOTMAP_ID) { @@ -824,37 +887,15 @@ void bar::handle(const evt::property_notify& evt) { // {{{ } } // }}} -/** - * Get the horizontal center pos - */ -int bar::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 bar::width_inner() { // {{{ - auto w = m_bar.width; - w -= m_borders[border::RIGHT].size; - w -= m_borders[border::LEFT].size; - return w; -} // }}} - /** * Handle alignment update */ void bar::on_alignment_change(alignment align) { // {{{ - if (align == m_bar.align) + if (align == m_opts.align) return; m_log.trace_x("bar: alignment_change(%i)", static_cast(align)); - m_bar.align = align; + m_opts.align = align; if (align == alignment::LEFT) { m_xpos = m_borders[border::LEFT].size; @@ -905,7 +946,7 @@ void bar::on_action_block_open(mousebtn btn, string cmd) { // {{{ m_log.trace_x("bar: action_block_open(%i, %s)", static_cast(btn), cmd); action_block action; action.active = true; - action.align = m_bar.align; + action.align = m_opts.align; action.button = btn; action.start_x = m_xpos; action.command = string_util::replace_all(cmd, ":", "\\:"); @@ -929,7 +970,7 @@ void bar::on_action_block_close(mousebtn btn) { // {{{ if (action.align == alignment::LEFT) { action.end_x = m_xpos; } else if (action.align == alignment::CENTER) { - int base_x = m_bar.width; + int base_x = m_opts.width; base_x -= m_borders[border::RIGHT].size; base_x /= 2; base_x += m_borders[border::LEFT].size; @@ -938,7 +979,7 @@ void bar::on_action_block_close(mousebtn btn) { // {{{ action.start_x = base_x - clickable_width / 2 + action.start_x / 2; action.end_x = action.start_x + clickable_width; } else if (action.align == alignment::RIGHT) { - int base_x = m_bar.width - m_borders[border::RIGHT].size; + int base_x = m_opts.width - m_borders[border::RIGHT].size; action.start_x = base_x - m_xpos + action.start_x; action.end_x = base_x; } @@ -951,8 +992,8 @@ void bar::on_action_block_close(mousebtn btn) { // {{{ * Handle color change */ void bar::on_color_change(gc gc_, color color_) { // {{{ - m_log.trace_x("bar: color_change(%i, %s -> #%07x)", static_cast(gc_), color_.source(), - static_cast(color_)); + m_log.trace_x( + "bar: color_change(%i, %s -> #%07x)", static_cast(gc_), color_.source(), static_cast(color_)); const uint32_t value_list[]{color_}; m_connection.change_gc(m_gcontexts.at(gc_), XCB_GC_FOREGROUND, value_list); @@ -985,13 +1026,15 @@ void bar::on_pixel_offset(int px) { // {{{ * Proess systray report */ void bar::on_tray_report(uint16_t slots) { // {{{ - if (m_tray.configured_slots != slots) { - m_log.trace("bar: tray_report(%lu)", slots); - m_tray.configured_slots = slots; + if (m_trayclients == slots) { + return; + } - if (!m_prevdata.empty()) { - parse(m_prevdata, true); - } + m_log.trace("bar: tray_report(%lu)", slots); + m_trayclients = slots; + + if (!m_prevdata.empty()) { + parse(m_prevdata, true); } } // }}} @@ -999,7 +1042,7 @@ void bar::on_tray_report(uint16_t slots) { // {{{ * Draw background onto the pixmap */ void bar::draw_background() { // {{{ - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), 0, 0, m_bar.width, m_bar.height); + draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), 0, 0, m_opts.width, m_opts.height); } // }}} /** @@ -1012,34 +1055,31 @@ void bar::draw_border(border border_) { // {{{ case border::TOP: if (m_borders[border::TOP].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BT), - m_borders[border::LEFT].size, 0, - m_bar.width - m_borders[border::LEFT].size - m_borders[border::RIGHT].size, - m_borders[border::TOP].size); + draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BT), m_borders[border::LEFT].size, 0, + m_opts.width - m_borders[border::LEFT].size - m_borders[border::RIGHT].size, m_borders[border::TOP].size); } break; case border::BOTTOM: if (m_borders[border::BOTTOM].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BB), - m_borders[border::LEFT].size, m_bar.height - m_borders[border::BOTTOM].size, - m_bar.width - m_borders[border::LEFT].size - m_borders[border::RIGHT].size, + draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BB), m_borders[border::LEFT].size, + m_opts.height - m_borders[border::BOTTOM].size, + m_opts.width - m_borders[border::LEFT].size - m_borders[border::RIGHT].size, m_borders[border::BOTTOM].size); } break; case border::LEFT: if (m_borders[border::LEFT].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BL), 0, 0, - m_borders[border::LEFT].size, m_bar.height); + draw_util::fill( + m_connection, m_pixmap, m_gcontexts.at(gc::BL), 0, 0, m_borders[border::LEFT].size, m_opts.height); } break; case border::RIGHT: if (m_borders[border::RIGHT].size > 0) { - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BR), - m_bar.width - m_borders[border::RIGHT].size, 0, m_borders[border::RIGHT].size, - m_bar.height); + draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BR), m_opts.width - m_borders[border::RIGHT].size, 0, + m_borders[border::RIGHT].size, m_opts.height); } break; @@ -1056,16 +1096,16 @@ void bar::draw_border(border border_) { // {{{ * Draw over- and underline onto the pixmap */ void bar::draw_lines(int x, int w) { // {{{ - if (!m_bar.lineheight) + if (!m_opts.lineheight) return; if (m_attributes & static_cast(attribute::o)) - draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, m_borders[border::TOP].size, - w, m_bar.lineheight); + draw_util::fill( + m_connection, m_pixmap, m_gcontexts.at(gc::OL), x, m_borders[border::TOP].size, w, m_opts.lineheight); if (m_attributes & static_cast(attribute::u)) draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::UL), x, - m_bar.height - m_borders[border::BOTTOM].size - m_bar.lineheight, w, m_bar.lineheight); + m_opts.height - m_borders[border::BOTTOM].size - m_opts.lineheight, w, m_opts.lineheight); } // }}} /** @@ -1074,28 +1114,27 @@ void bar::draw_lines(int x, int w) { // {{{ int bar::draw_shift(int x, int chr_width) { // {{{ int delta = chr_width; - if (m_bar.align == alignment::CENTER) { - int base_x = m_bar.width; + if (m_opts.align == alignment::CENTER) { + int base_x = m_opts.width; base_x -= m_borders[border::RIGHT].size; base_x /= 2; base_x += m_borders[border::LEFT].size; - m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), base_x - x / 2, 0, - base_x - (x + chr_width) / 2, 0, x, m_bar.height); + m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), base_x - x / 2, 0, base_x - (x + chr_width) / 2, + 0, x, m_opts.height); x = base_x - (x + chr_width) / 2 + x; delta /= 2; - } else if (m_bar.align == alignment::RIGHT) { - m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), m_bar.width - x, 0, - m_bar.width - x - chr_width, 0, x, m_bar.height); - x = m_bar.width - chr_width - m_borders[border::RIGHT].size; + } else if (m_opts.align == alignment::RIGHT) { + m_connection.copy_area(m_pixmap, m_pixmap, m_gcontexts.at(gc::FG), m_opts.width - x, 0, + m_opts.width - x - chr_width, 0, x, m_opts.height); + x = m_opts.width - chr_width - m_borders[border::RIGHT].size; } - draw_util::fill( - m_connection, m_pixmap, m_gcontexts.at(gc::BG), x, 0, m_bar.width - x, m_bar.height); + draw_util::fill(m_connection, m_pixmap, m_gcontexts.at(gc::BG), x, 0, m_opts.width - x, m_opts.height); // Translate pos of clickable areas - if (m_bar.align != alignment::LEFT) + if (m_opts.align != alignment::LEFT) for (auto&& action : m_actions) { - if (action.active || action.align != m_bar.align) + if (action.active || action.align != m_opts.align) continue; action.start_x -= delta; action.end_x -= delta; @@ -1122,11 +1161,11 @@ void bar::draw_character(uint16_t character) { // {{{ // Avoid odd glyph width's for center-aligned text // since it breaks the positioning of clickable area's - if (m_bar.align == alignment::CENTER && chr_width % 2) + if (m_opts.align == alignment::CENTER && chr_width % 2) chr_width++; auto x = draw_shift(m_xpos, chr_width); - auto y = m_bar.vertical_mid + font->height / 2 - font->descent + font->offset_y; + auto y = m_opts.vertical_mid + font->height / 2 - font->descent + font->offset_y; // m_log.trace("Draw char(%c, width: %i) at pos(%i,%i)", character, chr_width, x, y); @@ -1135,8 +1174,7 @@ void bar::draw_character(uint16_t character) { // {{{ XftDrawString16(m_xftdraw, &color, font->xft, x, y, &character, 1); } else { uint16_t ucs = ((character >> 8) | (character << 8)); - draw_util::xcb_poly_text_16_patched( - m_connection, m_pixmap, m_gcontexts.at(gc::FG), x, y, 1, &ucs); + draw_util::xcb_poly_text_16_patched(m_connection, m_pixmap, m_gcontexts.at(gc::FG), x, y, 1, &ucs); } draw_lines(x, chr_width); @@ -1171,11 +1209,11 @@ void bar::draw_textstring(const char* text, size_t len) { // {{{ // Avoid odd glyph width's for center-aligned text // since it breaks the positioning of clickable area's - if (m_bar.align == alignment::CENTER && chr_width % 2) + if (m_opts.align == alignment::CENTER && chr_width % 2) chr_width++; auto x = draw_shift(m_xpos, chr_width); - auto y = m_bar.vertical_mid + font->height / 2 - font->descent + font->offset_y; + auto y = m_opts.vertical_mid + font->height / 2 - font->descent + font->offset_y; // m_log.trace("Draw char(%c, width: %i) at pos(%i,%i)", character, chr_width, x, y); diff --git a/src/components/controller.cpp b/src/components/controller.cpp index 4df785b0..cd9dd96c 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include "components/controller.hpp" #include "components/signals.hpp" @@ -12,8 +12,8 @@ #include "modules/memory.hpp" #include "modules/menu.hpp" #include "modules/script.hpp" -#include "modules/text.hpp" #include "modules/temperature.hpp" +#include "modules/text.hpp" #include "modules/unsupported.hpp" #include "modules/xbacklight.hpp" #include "utils/process.hpp" @@ -95,16 +95,16 @@ void controller::bootstrap(bool writeback, bool dump_wmname) { m_log.trace("controller: Listen for events on the root window"); try { const uint32_t value_list[2]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY}; - m_connection.change_window_attributes_checked( - m_connection.root(), XCB_CW_EVENT_MASK, value_list); - } catch (const std::exception& err) { + m_connection.change_window_attributes_checked(m_connection.root(), XCB_CW_EVENT_MASK, value_list); + } catch (const exception& err) { throw application_error("Failed to change root window event mask: " + string{err.what()}); } try { m_log.trace("controller: Setup bar"); m_bar->bootstrap(m_writeback || dump_wmname); - } catch (const std::exception& err) { + m_bar->bootstrap_tray(); + } catch (const exception& err) { throw application_error("Failed to setup bar renderer: " + string{err.what()}); } @@ -292,7 +292,7 @@ void controller::wait_for_xevent() { if (evt != nullptr) { m_connection.dispatch_event(evt); } - } catch (const std::exception& err) { + } catch (const exception& err) { m_log.err("Error in X event loop: %s", err.what()); } } @@ -364,10 +364,10 @@ void controller::bootstrap_modules() { else throw application_error("Unknown module: " + module_name); - module->set_update_cb(bind(&eventloop::enqueue, m_eventloop.get(), - eventloop::entry_t{static_cast(event_type::UPDATE)})); - module->set_stop_cb(bind(&eventloop::enqueue, m_eventloop.get(), - eventloop::entry_t{static_cast(event_type::CHECK)})); + module->set_update_cb( + bind(&eventloop::enqueue, m_eventloop.get(), eventloop::entry_t{static_cast(event_type::UPDATE)})); + module->set_stop_cb( + bind(&eventloop::enqueue, m_eventloop.get(), eventloop::entry_t{static_cast(event_type::CHECK)})); module->setup(); diff --git a/src/components/types.cpp b/src/components/types.cpp deleted file mode 100644 index d665f06f..00000000 --- a/src/components/types.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "components/types.hpp" diff --git a/src/modules/bspwm.cpp b/src/modules/bspwm.cpp index 905bb3df..2ff63172 100644 --- a/src/modules/bspwm.cpp +++ b/src/modules/bspwm.cpp @@ -13,46 +13,40 @@ namespace modules { GET_CONFIG_VALUE(name(), m_pinworkspaces, "pin-workspaces"); // Add formats and create components - m_formatter->add( - DEFAULT_FORMAT, TAG_LABEL_STATE, {TAG_LABEL_STATE}, {TAG_LABEL_MONITOR, TAG_LABEL_MODE}); + m_formatter->add(DEFAULT_FORMAT, TAG_LABEL_STATE, {TAG_LABEL_STATE}, {TAG_LABEL_MONITOR, TAG_LABEL_MODE}); if (m_formatter->has(TAG_LABEL_MONITOR)) { m_monitorlabel = load_optional_label(m_conf, name(), "label-monitor", DEFAULT_MONITOR_LABEL); } if (m_formatter->has(TAG_LABEL_STATE)) { - m_statelabels.insert(make_pair(state_ws::WORKSPACE_ACTIVE, - load_optional_label(m_conf, name(), "label-active", DEFAULT_WS_LABEL))); - m_statelabels.insert(make_pair(state_ws::WORKSPACE_OCCUPIED, - load_optional_label(m_conf, name(), "label-occupied", DEFAULT_WS_LABEL))); - m_statelabels.insert(make_pair(state_ws::WORKSPACE_URGENT, - load_optional_label(m_conf, name(), "label-urgent", DEFAULT_WS_LABEL))); - m_statelabels.insert(make_pair(state_ws::WORKSPACE_EMPTY, - load_optional_label(m_conf, name(), "label-empty", DEFAULT_WS_LABEL))); + m_statelabels.insert( + make_pair(state_ws::WORKSPACE_ACTIVE, load_optional_label(m_conf, name(), "label-active", DEFAULT_WS_LABEL))); m_statelabels.insert(make_pair( - state_ws::WORKSPACE_DIMMED, load_optional_label(m_conf, name(), "label-dimmed"))); + state_ws::WORKSPACE_OCCUPIED, load_optional_label(m_conf, name(), "label-occupied", DEFAULT_WS_LABEL))); + m_statelabels.insert( + make_pair(state_ws::WORKSPACE_URGENT, load_optional_label(m_conf, name(), "label-urgent", DEFAULT_WS_LABEL))); + m_statelabels.insert( + make_pair(state_ws::WORKSPACE_EMPTY, load_optional_label(m_conf, name(), "label-empty", DEFAULT_WS_LABEL))); + m_statelabels.insert(make_pair(state_ws::WORKSPACE_DIMMED, load_optional_label(m_conf, name(), "label-dimmed"))); } if (m_formatter->has(TAG_LABEL_MODE)) { - m_modelabels.insert(make_pair( - state_mode::MODE_LAYOUT_MONOCLE, load_optional_label(m_conf, name(), "label-monocle"))); - m_modelabels.insert(make_pair( - state_mode::MODE_LAYOUT_TILED, load_optional_label(m_conf, name(), "label-tiled"))); - m_modelabels.insert(make_pair(state_mode::MODE_STATE_FULLSCREEN, - load_optional_label(m_conf, name(), "label-fullscreen"))); - m_modelabels.insert(make_pair( - state_mode::MODE_STATE_FLOATING, load_optional_label(m_conf, name(), "label-floating"))); - m_modelabels.insert(make_pair( - state_mode::MODE_NODE_LOCKED, load_optional_label(m_conf, name(), "label-locked"))); - m_modelabels.insert(make_pair( - state_mode::MODE_NODE_STICKY, load_optional_label(m_conf, name(), "label-sticky"))); - m_modelabels.insert(make_pair( - state_mode::MODE_NODE_PRIVATE, load_optional_label(m_conf, name(), "label-private"))); + m_modelabels.insert( + make_pair(state_mode::MODE_LAYOUT_MONOCLE, load_optional_label(m_conf, name(), "label-monocle"))); + m_modelabels.insert(make_pair(state_mode::MODE_LAYOUT_TILED, load_optional_label(m_conf, name(), "label-tiled"))); + m_modelabels.insert( + make_pair(state_mode::MODE_STATE_FULLSCREEN, load_optional_label(m_conf, name(), "label-fullscreen"))); + m_modelabels.insert( + make_pair(state_mode::MODE_STATE_FLOATING, load_optional_label(m_conf, name(), "label-floating"))); + m_modelabels.insert(make_pair(state_mode::MODE_NODE_LOCKED, load_optional_label(m_conf, name(), "label-locked"))); + m_modelabels.insert(make_pair(state_mode::MODE_NODE_STICKY, load_optional_label(m_conf, name(), "label-sticky"))); + m_modelabels.insert( + make_pair(state_mode::MODE_NODE_PRIVATE, load_optional_label(m_conf, name(), "label-private"))); } m_icons = iconset_t{new iconset()}; - m_icons->add( - DEFAULT_WS_ICON, icon_t{new icon(m_conf.get(name(), DEFAULT_WS_ICON, ""))}); + m_icons->add(DEFAULT_WS_ICON, icon_t{new icon(m_conf.get(name(), DEFAULT_WS_ICON, ""))}); for (auto workspace : m_conf.get_list(name(), "ws-icon", {})) { auto vec = string_util::split(workspace, ';'); @@ -118,17 +112,17 @@ namespace modules { const auto needle_active = ":M" + m_bar.monitor->name + ":"; const auto needle_inactive = ":m" + m_bar.monitor->name + ":"; - if ((pos = data.find(prefix)) != std::string::npos) + if ((pos = data.find(prefix)) != string::npos) data = data.replace(pos, prefix.length(), ":"); - if ((pos = data.find(needle_active)) != std::string::npos) + if ((pos = data.find(needle_active)) != string::npos) data.erase(0, pos + 1); - if ((pos = data.find(needle_inactive)) != std::string::npos) + if ((pos = data.find(needle_inactive)) != string::npos) data.erase(0, pos + 1); - if ((pos = data.find(":m", 1)) != std::string::npos) + if ((pos = data.find(":m", 1)) != string::npos) data.erase(pos); - if ((pos = data.find(":M", 1)) != std::string::npos) + if ((pos = data.find(":M", 1)) != string::npos) data.erase(pos); - } else if ((pos = data.find(prefix)) != std::string::npos) { + } else if ((pos = data.find(prefix)) != string::npos) { data = data.replace(pos, prefix.length(), ":"); } else { return false; @@ -259,7 +253,7 @@ namespace modules { label->replace_token("%icon%", icon->get()); label->replace_token("%index%", to_string(++workspace_n)); - m_monitors.back()->workspaces.emplace_back(make_pair(workspace_flag, std::move(label))); + m_monitors.back()->workspaces.emplace_back(make_pair(workspace_flag, move(label))); } if (mode_flag != state_mode::MODE_NONE && !m_modelabels.empty()) { @@ -325,36 +319,35 @@ namespace modules { return false; } - try { - auto send_command = [this](string payload_cmd, string log_info) { + auto send_command = [this](string payload_cmd, string log_info) { + try { auto ipc = bspwm_util::make_connection(); auto payload = bspwm_util::make_payload(payload_cmd); m_log.info("%s: %s", name(), log_info); ipc->send(payload->data, payload->len, 0); ipc->disconnect(); - }; - if (cmd.compare(0, strlen(EVENT_CLICK), EVENT_CLICK) == 0) { - cmd.erase(0, strlen(EVENT_CLICK)); + } catch (const system_error& err) { + m_log.err("%s: %s", name(), err.what()); + } + }; - size_t separator = string_util::find_nth(cmd, 0, "+", 1); - size_t monitor_n = std::atoi(cmd.substr(0, separator).c_str()); - string workspace_n = cmd.substr(separator + 1); + if (cmd.compare(0, strlen(EVENT_CLICK), EVENT_CLICK) == 0) { + cmd.erase(0, strlen(EVENT_CLICK)); - if (monitor_n < m_monitors.size()) { - send_command("desktop -f " + m_monitors[monitor_n]->name + ":^" + workspace_n, + size_t separator = string_util::find_nth(cmd, 0, "+", 1); + size_t monitor_n = std::atoi(cmd.substr(0, separator).c_str()); + string workspace_n = cmd.substr(separator + 1); + + if (monitor_n < m_monitors.size()) { + send_command("desktop -f " + m_monitors[monitor_n]->name + ":^" + workspace_n, "Sending desktop focus command to ipc handler"); - } else { - m_log.err("%s: Invalid monitor index in command: %s", name(), cmd); - } + } else { + m_log.err("%s: Invalid monitor index in command: %s", name(), cmd); } - else if (cmd.compare(0, strlen(EVENT_SCROLL_UP), EVENT_SCROLL_UP) == 0) { - send_command("desktop -f next", "Sending desktop next command to ipc handler"); - } - else if (cmd.compare(0, strlen(EVENT_SCROLL_DOWN), EVENT_SCROLL_DOWN) == 0) { - send_command("desktop -f prev", "Sending desktop prev command to ipc handler"); - } - } catch (const system_error& err) { - m_log.err("%s: %s", name(), err.what()); + } else if (cmd.compare(0, strlen(EVENT_SCROLL_UP), EVENT_SCROLL_UP) == 0) { + send_command("desktop -f next", "Sending desktop next command to ipc handler"); + } else if (cmd.compare(0, strlen(EVENT_SCROLL_DOWN), EVENT_SCROLL_DOWN) == 0) { + send_command("desktop -f prev", "Sending desktop prev command to ipc handler"); } return true; diff --git a/src/x11/graphics.cpp b/src/x11/graphics.cpp index 2f6147d1..f6b530eb 100644 --- a/src/x11/graphics.cpp +++ b/src/x11/graphics.cpp @@ -12,9 +12,12 @@ namespace graphics_util { /** * Create a basic window */ - bool create_window(connection& conn, xcb_window_t* win, int16_t x, int16_t y, uint16_t w, uint16_t h) { + bool create_window(connection& conn, xcb_window_t* win, int16_t x, int16_t y, uint16_t w, uint16_t h, xcb_window_t root) { + if (!root) { + root = conn.screen()->root; + } + try { - auto root = conn.screen()->root; auto copy = XCB_COPY_FROM_PARENT; *win = conn.generate_id(); conn.create_window_checked(copy, *win, root, x, y, w, h, 0, copy, copy, 0, nullptr); @@ -59,7 +62,7 @@ namespace graphics_util { uint32_t mask = 0; uint32_t values[32]; - XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, false); + XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 1); xutils::pack_values(mask, ¶ms, values); *gc = conn.generate_id(); diff --git a/src/x11/tray.cpp b/src/x11/tray.cpp index 45d269e6..7f372055 100644 --- a/src/x11/tray.cpp +++ b/src/x11/tray.cpp @@ -1,65 +1,71 @@ +#include #include -#include "x11/tray.hpp" +#include "components/signals.hpp" #include "utils/color.hpp" +#include "utils/memory.hpp" +#include "utils/process.hpp" +#include "x11/connection.hpp" #include "x11/draw.hpp" +#include "x11/tray.hpp" #include "x11/window.hpp" #include "x11/wm.hpp" +#include "x11/xembed.hpp" LEMONBUDDY_NS -// trayclient {{{ +// implementation : tray_client {{{ -trayclient::trayclient(connection& conn, xcb_window_t win, uint16_t w, uint16_t h) +tray_client::tray_client(connection& conn, xcb_window_t win, uint16_t w, uint16_t h) : m_connection(conn), m_window(win), m_width(w), m_height(h) { m_xembed = memory_util::make_malloc_ptr(); m_xembed->version = XEMBED_VERSION; m_xembed->flags = XEMBED_MAPPED; } -trayclient::~trayclient() { +tray_client::~tray_client() { xembed::unembed(m_connection, window(), m_connection.root()); } /** * Match given window against client window */ -bool trayclient::match(const xcb_window_t& win) const { // {{{ +bool tray_client::match(const xcb_window_t& win) const { // {{{ return win == m_window; } // }}} /** * Get client window mapped state */ -bool trayclient::mapped() const { // {{{ +bool tray_client::mapped() const { // {{{ return m_mapped; } // }}} /** * Set client window mapped state */ -void trayclient::mapped(bool state) { // {{{ +void tray_client::mapped(bool state) { // {{{ m_mapped = state; } // }}} /** * Get client window */ -xcb_window_t trayclient::window() const { // {{{ +xcb_window_t tray_client::window() const { // {{{ return m_window; } // }}} /** * Get xembed data pointer */ -xembed_data* trayclient::xembed() const { // {{{ +xembed_data* tray_client::xembed() const { // {{{ return m_xembed.get(); } // }}} /** * Make sure that the window mapping state is correct */ -void trayclient::ensure_state() const { // {{{ +void tray_client::ensure_state() const { // {{{ if (!mapped() && ((xembed()->flags & XEMBED_MAPPED) == XEMBED_MAPPED)) { m_connection.map_window_checked(window()); } else if (mapped() && ((xembed()->flags & XEMBED_MAPPED) != XEMBED_MAPPED)) { @@ -70,7 +76,7 @@ void trayclient::ensure_state() const { // {{{ /** * Configure window size */ -void trayclient::reconfigure(int16_t x, int16_t y) const { // {{{ +void tray_client::reconfigure(int16_t x, int16_t y) const { // {{{ uint32_t configure_mask = 0; uint32_t configure_values[7]; xcb_params_configure_window_t configure_params; @@ -87,13 +93,13 @@ void trayclient::reconfigure(int16_t x, int16_t y) const { // {{{ /** * Respond to client resize requests */ -void trayclient::configure_notify(int16_t x, int16_t y) const { // {{{ +void tray_client::configure_notify(int16_t x, int16_t y) const { // {{{ auto notify = memory_util::make_malloc_ptr(32); notify->response_type = XCB_CONFIGURE_NOTIFY; notify->event = m_window; notify->window = m_window; notify->override_redirect = false; - notify->above_sibling = XCB_NONE; + notify->above_sibling = 0; notify->x = x; notify->y = y; notify->width = m_width; @@ -105,56 +111,59 @@ void trayclient::configure_notify(int16_t x, int16_t y) const { // {{{ } // }}} // }}} -// traymanager {{{ +// implementation : tray_manager {{{ -traymanager::traymanager(connection& conn, const logger& logger) - : m_connection(conn), m_log(logger) { +tray_manager::tray_manager(connection& conn, const logger& logger) : m_connection(conn), m_log(logger) { m_connection.attach_sink(this, 2); m_sinkattached = true; } -traymanager::~traymanager() { +tray_manager::~tray_manager() { if (m_activated) deactivate(); if (m_sinkattached) m_connection.detach_sink(this, 2); - if (m_pixmap) - xcb_free_pixmap(m_connection, m_pixmap); - if (m_gc) - xcb_free_gc(m_connection, m_gc); } +/** + * Get the settings container + */ +const tray_settings tray_manager::settings() const { // {{{ + return m_opts; +} // }}} + /** * Initialize data */ -void traymanager::bootstrap(tray_settings settings) { // {{{ - m_settings = settings; +void tray_manager::bootstrap(tray_settings settings) { // {{{ + m_opts = settings; query_atom(); } // }}} /** * Activate systray management */ -void traymanager::activate() { // {{{ +void tray_manager::activate() { // {{{ if (m_activated) { return; } - if (m_tray == XCB_NONE) { - 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"); - return; - } - } - - m_log.info("Activating traymanager"); + m_log.info("Activating tray_manager"); m_activated = true; + try { + create_window(); + create_bg(); + restack_window(); + set_wmhints(); + set_traycolors(); + } catch (const exception& err) { + m_log.err(err.what()); + m_log.err("Cannot activate tray_manager... failed to setup window"); + m_activated = false; + return; + } + if (!m_sinkattached) { m_connection.attach_sink(this, 2); m_sinkattached = true; @@ -162,13 +171,12 @@ void traymanager::activate() { // {{{ // Listen for visibility change events on the bar window if (!m_restacked && !g_signals::bar::visibility_change) { - g_signals::bar::visibility_change = - bind(&traymanager::bar_visibility_change, this, std::placeholders::_1); + g_signals::bar::visibility_change = bind(&tray_manager::bar_visibility_change, this, std::placeholders::_1); } // Listen for redraw events on the bar window if (!g_signals::bar::redraw) { - g_signals::bar::redraw = bind(&traymanager::refresh_window, this); + g_signals::bar::redraw = bind(&tray_manager::refresh_window, this); } // Attempt to get control of the systray selection then @@ -178,7 +186,7 @@ void traymanager::activate() { // {{{ // If replacing an existing manager or if re-activating from getting // replaced, we delay the notification broadcast to allow the clients // to get unembedded... - if (m_othermanager != XCB_NONE) + if (m_othermanager) this_thread::sleep_for(1s); notify_clients(); @@ -189,12 +197,12 @@ void traymanager::activate() { // {{{ /** * Deactivate systray management */ -void traymanager::deactivate() { // {{{ +void tray_manager::deactivate() { // {{{ if (!m_activated) { return; } - m_log.info("Deactivating traymanager"); + m_log.info("Deactivating tray_manager"); m_activated = false; if (m_delayed_activation.joinable()) @@ -221,7 +229,7 @@ void traymanager::deactivate() { // {{{ m_log.trace("tray: Unembed clients"); m_clients.clear(); - if (m_tray != XCB_NONE) { + if (m_tray) { if (m_mapped) { m_log.trace("tray: Unmap window"); m_connection.unmap_window(m_tray); @@ -230,20 +238,32 @@ void traymanager::deactivate() { // {{{ m_log.trace("tray: Destroy window"); m_connection.destroy_window(m_tray); - m_tray = XCB_NONE; m_hidden = false; } + if (m_pixmap) { + m_connection.free_pixmap(m_pixmap); + } + + if (m_gc) { + m_connection.free_gc(m_pixmap); + } + + m_tray = 0; + m_pixmap = 0; + m_gc = 0; + m_rootpixmap.pixmap = 0; + m_connection.flush(); } // }}} /** * Reconfigure tray */ -void traymanager::reconfigure() { // {{{ +void tray_manager::reconfigure() { // {{{ // Skip if tray window doesn't exist or if it's // in pseudo-hidden state - if (m_tray == XCB_NONE || m_hidden) { + if (!m_tray || m_hidden) { return; } @@ -255,22 +275,31 @@ void traymanager::reconfigure() { // {{{ try { reconfigure_clients(); - reconfigure_window(); - reconfigure_bg(); - refresh_window(); - m_connection.flush(); - } catch (const std::exception& err) { - m_log.err("Failed to reconfigure tray (%s)", err.what()); - return; + } catch (const exception& err) { + m_log.err("Failed to reconfigure tray clients (%s)", err.what()); } + try { + reconfigure_window(); + } catch (const exception& err) { + m_log.err("Failed to reconfigure tray window (%s)", err.what()); + } + + try { + reconfigure_bg(); + } catch (const exception& err) { + m_log.err("Failed to reconfigure tray background (%s)", err.what()); + } + + refresh_window(); + m_connection.flush(); - m_settings.configured_slots = mapped_clients(); + m_opts.configured_slots = mapped_clients(); // Report status if (g_signals::tray::report_slotcount) { - g_signals::tray::report_slotcount(m_settings.configured_slots); + g_signals::tray::report_slotcount(m_opts.configured_slots); } guard.unlock(); @@ -281,10 +310,10 @@ void traymanager::reconfigure() { // {{{ /** * Reconfigure container window */ -void traymanager::reconfigure_window() { // {{{ +void tray_manager::reconfigure_window() { // {{{ m_log.trace("tray: Reconfigure window"); - if (m_tray == XCB_NONE) { + if (!m_tray) { return; } @@ -313,7 +342,7 @@ void traymanager::reconfigure_window() { // {{{ auto width = calculate_w(); auto x = calculate_x(width); - if (m_settings.configured_w == width && m_settings.configured_x == x) { + if (m_opts.configured_w == width && m_opts.configured_x == x) { m_log.trace("tray: Reconfigure window / ignoring unchanged values w=%d x=%d", width, x); return; } @@ -332,17 +361,17 @@ void traymanager::reconfigure_window() { // {{{ m_connection.configure_window_checked(m_tray, mask, values); - m_settings.configured_w = width; - m_settings.configured_x = x; + m_opts.configured_w = width; + m_opts.configured_x = x; } // }}} /** * Reconfigure clients */ -void traymanager::reconfigure_clients() { // {{{ +void tray_manager::reconfigure_clients() { // {{{ m_log.trace("tray: Reconfigure clients"); - uint32_t x = m_settings.spacing; + uint32_t x = m_opts.spacing; for (auto it = m_clients.rbegin(); it != m_clients.rend(); it++) { auto client = *it; @@ -351,7 +380,7 @@ void traymanager::reconfigure_clients() { // {{{ client->ensure_state(); client->reconfigure(x, calculate_client_y()); - x += m_settings.width + m_settings.spacing; + x += m_opts.width + m_opts.spacing; } catch (const xpp::x::error::window& err) { remove_client(client, false); } @@ -361,84 +390,69 @@ void traymanager::reconfigure_clients() { // {{{ /** * Reconfigure root pixmap */ -void traymanager::reconfigure_bg(bool realloc) { // {{{ - if (!m_settings.transparent) { +void tray_manager::reconfigure_bg(bool realloc) { // {{{ + if (!m_opts.transparent || m_clients.empty() || !m_mapped) { return; } - if (m_clients.empty() || !m_mapped) { + auto w = calculate_w(); + auto h = calculate_h(); + + if ((!w || (w == m_prevwidth && h == m_prevheight)) && !realloc) { return; } - auto width = calculate_w(); - auto height = calculate_h(); + m_log.trace("tray: Reconfigure bg (realloc=%i)", realloc); - if (!realloc) { - if (width == m_prevwidth && height == m_prevheight) { + if (!m_rootpixmap.pixmap && !get_root_pixmap(m_connection, &m_rootpixmap)) { + return m_log.err("Failed to get root pixmap for tray background (realloc=%i)", realloc); + } else if (realloc && !get_root_pixmap(m_connection, &m_rootpixmap)) { + 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, + m_rootpixmap.height, m_rootpixmap.x, m_rootpixmap.y, m_tray, m_pixmap, m_gc); + + m_prevwidth = w; + m_prevheight = h; + + auto x = calculate_x(w); + auto y = calculate_y(); + + auto px = m_rootpixmap.x + x; + auto py = m_rootpixmap.y + y; + + if (realloc) { + vector image_data; + uint8_t image_depth; + + try { + auto image_reply = + m_connection.get_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_rootpixmap.pixmap, px, py, w, h, XCB_COPY_PLANE); + image_depth = image_reply->depth; + std::back_insert_iterator back_it(image_data); + std::copy(image_reply.data().begin(), image_reply.data().end(), back_it); + } catch (const exception& err) { + m_log.err("Failed to get slice of root pixmap (%s)", err.what()); return; - } else if (!width) { + } + + try { + m_connection.put_image_checked( + XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_gc, w, h, 0, 0, 0, image_depth, image_data.size(), image_data.data()); + } catch (const exception& err) { + m_log.err("Failed to store slice of root pixmap (%s)", err.what()); return; } } - m_log.trace("tray: Reconfigure bg"); - - if (!m_rootpixmap.pixmap || realloc) - graphics_util::get_root_pixmap(m_connection, &m_rootpixmap); - if (!m_pixmap) - graphics_util::simple_pixmap(m_connection, m_tray, width, height, &m_pixmap); - if (!m_gc) - graphics_util::simple_gc(m_connection, m_pixmap, &m_gc); - - if (!m_rootpixmap.pixmap) { - m_log.warn("Failed to get root pixmap for tray background"); - return; - } - - m_log.trace("tray: rootpixmap=%x (%dx%d+%d+%d), tray=%x, pixmap=%x, gc=%x", - m_rootpixmap.pixmap, - m_rootpixmap.width, - m_rootpixmap.height, - m_rootpixmap.x, - m_rootpixmap.y, - m_tray, m_pixmap, m_gc); - - m_prevwidth = width; - m_prevheight = height; - - auto x = calculate_x(width); - auto y = calculate_y(); - - m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_gc, x, y, 0, 0, width, height); - - auto image_reply = m_connection.get_image( - XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, 0, 0, width, height, XAllPlanes()); - - vector image_data; - std::back_insert_iterator back_it(image_data); - std::copy(image_reply.data().begin(), image_reply.data().end(), back_it); - uint8_t* data = image_data.data(); - - m_connection.put_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_gc, width, height, 0, 0, 0, - image_reply->depth, image_data.size(), data); - - m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_gc, x, y, 0, 0, width, height); - - uint32_t mask = 0; - uint32_t values[16]; - xcb_params_cw_t params; - XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, m_pixmap); - xutils::pack_values(mask, ¶ms, values); - m_connection.change_window_attributes_checked(m_tray, mask, values); - - m_connection.copy_area(m_pixmap, m_tray, m_gc, x, y, 0, 0, width, height); + m_connection.copy_area_checked(m_rootpixmap.pixmap, m_pixmap, m_gc, px, py, 0, 0, w, h); } // }}} /** - * Refresh the bar window by clearing and copying - * the root pixmap onto it + * Refresh the bar window by clearing it along with each client window */ -void traymanager::refresh_window() { // {{{ +void tray_manager::refresh_window() { // {{{ if (!m_mtx.try_lock()) { return; } @@ -453,14 +467,15 @@ void traymanager::refresh_window() { // {{{ auto width = calculate_w(); auto height = calculate_h(); - auto x = calculate_x(width); - auto y = calculate_y(); + + if (m_opts.transparent && !m_rootpixmap.pixmap) { + draw_util::fill(m_connection, m_pixmap, m_gc, 0, 0, width, height); + } m_connection.clear_area(1, m_tray, 0, 0, width, height); - if (m_rootpixmap.pixmap && m_gc && m_pixmap) { - // draw_util::fill(m_connection, m_pixmap, m_gc, 0, 0, width, height); - m_connection.copy_area(m_pixmap, m_tray, m_gc, x, y, 0, 0, width, height); + for (auto&& client : m_clients) { + m_connection.clear_area(1, client->window(), 0, 0, m_opts.width, height); } m_connection.flush(); @@ -469,7 +484,7 @@ void traymanager::refresh_window() { // {{{ /** * Find the systray selection atom */ -void traymanager::query_atom() { // {{{ +void tray_manager::query_atom() { // {{{ m_log.trace("tray: Find systray selection atom for the default screen"); string name{"_NET_SYSTEM_TRAY_S" + to_string(m_connection.default_screen())}; auto reply = m_connection.intern_atom(false, name.length(), name.c_str()); @@ -479,7 +494,7 @@ void traymanager::query_atom() { // {{{ /** * Create tray window */ -void traymanager::create_window() { // {{{ +void tray_manager::create_window() { // {{{ auto scr = m_connection.screen(); auto w = calculate_w(); auto h = calculate_h(); @@ -497,39 +512,86 @@ void traymanager::create_window() { // {{{ uint32_t values[16]; xcb_params_cw_t params; - if (!m_settings.transparent) { - XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixel, m_settings.background); - XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, m_settings.background); + if (!m_opts.transparent) { + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixel, m_opts.background); + XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, m_opts.background); } XCB_AUX_ADD_PARAM(&mask, ¶ms, backing_store, XCB_BACKING_STORE_WHEN_MAPPED); XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, true); - XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY); + XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY); xutils::pack_values(mask, ¶ms, values); - m_connection.create_window_checked(scr->root_depth, m_tray, scr->root, x, y, w, h, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, mask, values); + + m_connection.create_window_checked( + scr->root_depth, m_tray, scr->root, x, y, w, h, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, mask, values); +} // }}} + +/** + * Create tray window background components + */ +void tray_manager::create_bg(bool realloc) { // {{{ + if (!m_opts.transparent) { + return; + } + + if (!realloc && m_pixmap && m_gc && m_rootpixmap.pixmap) { + return; + } + + if (realloc && m_pixmap) { + m_connection.free_pixmap(m_pixmap); + m_pixmap = 0; + } + + if (realloc && m_gc) { + m_connection.free_gc(m_gc); + m_gc = 0; + } + + auto w = m_opts.width_max; + auto h = calculate_h(); + + if (!m_pixmap && !graphics_util::create_pixmap(m_connection, m_tray, w, h, &m_pixmap)) { + return m_log.err("Failed to create pixmap for tray background"); + } else if (!m_gc && !graphics_util::create_gc(m_connection, m_pixmap, &m_gc)) { + return m_log.err("Failed to create gcontext for tray background"); + } try { - // Put the tray window above the defined sibling in the window stack - if (m_settings.sibling != 0) { - m_log.trace("tray: Restacking tray window"); + uint32_t mask = 0; + uint32_t values[16]; + xcb_params_cw_t params; + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, m_pixmap); + xutils::pack_values(mask, ¶ms, values); + m_connection.change_window_attributes_checked(m_tray, mask, values); + } catch (const exception& err) { + m_log.err("Failed to set tray window back pixmap (%s)", err.what()); + } +} // }}} - uint32_t configure_mask = 0; - uint32_t configure_values[7]; - xcb_params_configure_window_t configure_params; +/** + * Put tray window above the defined sibling in the window stack + */ +void tray_manager::restack_window() { // {{{ + if (!m_opts.sibling) { + return; + } - XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, sibling, m_settings.sibling); - XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, stack_mode, XCB_STACK_MODE_ABOVE); + try { + m_log.trace("tray: Restacking tray window"); - xutils::pack_values(configure_mask, &configure_params, configure_values); - m_connection.configure_window_checked(m_tray, configure_mask, configure_values); + uint32_t mask = 0; + uint32_t values[7]; + xcb_params_configure_window_t params; - m_restacked = true; - } - } catch (const std::exception& err) { - auto id = m_connection.id(m_settings.sibling); + XCB_AUX_ADD_PARAM(&mask, ¶ms, sibling, m_opts.sibling); + XCB_AUX_ADD_PARAM(&mask, ¶ms, stack_mode, XCB_STACK_MODE_ABOVE); + xutils::pack_values(mask, ¶ms, values); + m_connection.configure_window_checked(m_tray, mask, values); + m_restacked = true; + } catch (const exception& err) { + auto id = m_connection.id(m_opts.sibling); m_log.trace("tray: Failed to put tray above %s in the stack (%s)", id, err.what()); } } // }}} @@ -537,7 +599,7 @@ void traymanager::create_window() { // {{{ /** * Set window WM hints */ -void traymanager::set_wmhints() { // {{{ +void tray_manager::set_wmhints() { // {{{ m_log.trace("tray: Set window WM_NAME / WM_CLASS", m_connection.id(m_tray)); xcb_icccm_set_wm_name(m_connection, m_tray, XCB_ATOM_STRING, 8, 22, TRAY_WM_NAME); xcb_icccm_set_wm_class(m_connection, m_tray, 15, TRAY_WM_CLASS); @@ -558,8 +620,8 @@ void traymanager::set_wmhints() { // {{{ m_log.trace("tray: Set window _NET_WM_STATE"); vector states; states.emplace_back(_NET_WM_STATE_SKIP_TASKBAR); - m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_STATE, XCB_ATOM_ATOM, 32, - states.size(), states.data()); + m_connection.change_property( + XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_STATE, XCB_ATOM_ATOM, 32, states.size(), states.data()); m_log.trace("tray: Set window _NET_WM_PID"); wm_util::set_wmpid(m_connection, m_tray, getpid()); @@ -574,12 +636,12 @@ void traymanager::set_wmhints() { // {{{ /** * Set color atom used by clients when determing icon theme */ -void traymanager::set_traycolors() { // {{{ - m_log.trace("tray: Set _NET_SYSTEM_TRAY_COLORS to %x", m_settings.background); +void tray_manager::set_traycolors() { // {{{ + m_log.trace("tray: Set _NET_SYSTEM_TRAY_COLORS to %x", m_opts.background); - auto r = color_util::red_channel(m_settings.background); - auto g = color_util::green_channel(m_settings.background); - auto b = color_util::blue_channel(m_settings.background); + auto r = color_util::red_channel(m_opts.background); + auto g = color_util::green_channel(m_opts.background); + auto b = color_util::blue_channel(m_opts.background); const uint32_t colors[12] = { r, g, b, // normal @@ -595,13 +657,13 @@ void traymanager::set_traycolors() { // {{{ /** * Acquire the systray selection */ -void traymanager::acquire_selection() { // {{{ +void tray_manager::acquire_selection() { // {{{ xcb_window_t owner = m_connection.get_selection_owner_unchecked(m_atom)->owner; if (owner == m_tray) { m_log.info("tray: Already managing the systray selection"); return; - } else if ((m_othermanager = owner) != XCB_NONE) { + } else if ((m_othermanager = owner)) { m_log.info("Replacing selection manager %s", m_connection.id(owner)); } @@ -615,7 +677,7 @@ void traymanager::acquire_selection() { // {{{ /** * Notify pending clients about the new systray MANAGER */ -void traymanager::notify_clients() { // {{{ +void tray_manager::notify_clients() { // {{{ m_log.trace("tray: Broadcast new selection manager to pending clients"); auto message = m_connection.make_client_message(MANAGER, m_connection.root()); message->data.data32[0] = XCB_CURRENT_TIME; @@ -626,10 +688,10 @@ void traymanager::notify_clients() { // {{{ /** * Track changes to the given selection owner - * If it gets destroyed or goes away we can reactivate the traymanager + * If it gets destroyed or goes away we can reactivate the tray_manager */ -void traymanager::track_selection_owner(xcb_window_t owner) { // {{{ - if (owner == XCB_NONE) +void tray_manager::track_selection_owner(xcb_window_t owner) { // {{{ + if (!owner) return; m_log.trace("tray: Listen for events on the new selection window"); const uint32_t value_list[1]{XCB_EVENT_MASK_STRUCTURE_NOTIFY}; @@ -639,7 +701,7 @@ void traymanager::track_selection_owner(xcb_window_t owner) { // {{{ /** * Process client docking request */ -void traymanager::process_docking_request(xcb_window_t win) { // {{{ +void tray_manager::process_docking_request(xcb_window_t win) { // {{{ auto client = find_client(win); if (client) { @@ -649,8 +711,7 @@ void traymanager::process_docking_request(xcb_window_t win) { // {{{ m_log.info("Processing docking request from %s", m_connection.id(win)); - m_clients.emplace_back( - make_shared(m_connection, win, m_settings.width, m_settings.height)); + m_clients.emplace_back(make_shared(m_connection, win, m_opts.width, m_opts.height)); client = m_clients.back(); try { @@ -670,14 +731,8 @@ void traymanager::process_docking_request(xcb_window_t win) { // {{{ uint32_t mask = 0; uint32_t values[16]; xcb_params_cw_t params; - - XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, - XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY); - - if (m_settings.transparent) { - XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, XCB_BACK_PIXMAP_PARENT_RELATIVE); - } - + XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY); + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, XCB_BACK_PIXMAP_PARENT_RELATIVE); xutils::pack_values(mask, ¶ms, values); m_connection.change_window_attributes_checked(client->window(), mask, values); } @@ -710,7 +765,7 @@ void traymanager::process_docking_request(xcb_window_t win) { // {{{ * This is used as a fallback in case the window restacking fails. It will * toggle the tray window whenever the visibility of the bar window changes. */ -void traymanager::bar_visibility_change(bool state) { // {{{ +void tray_manager::bar_visibility_change(bool state) { // {{{ if (m_hidden == !state) { return; } @@ -733,31 +788,31 @@ void traymanager::bar_visibility_change(bool state) { // {{{ /** * Calculate x position of tray window */ -int16_t traymanager::calculate_x(uint16_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); +int16_t tray_manager::calculate_x(uint16_t width) const { // {{{ + auto x = m_opts.orig_x; + if (m_opts.align == alignment::RIGHT) + x -= ((m_opts.width + m_opts.spacing) * m_clients.size() + m_opts.spacing); + else if (m_opts.align == alignment::CENTER) + x -= (width / 2) - (m_opts.width / 2); return x; } // }}} /** * Calculate y position of tray window */ -int16_t traymanager::calculate_y() const { // {{{ - return m_settings.orig_y; +int16_t tray_manager::calculate_y() const { // {{{ + return m_opts.orig_y; } // }}} /** * Calculate width of tray window */ -uint16_t traymanager::calculate_w() const { // {{{ - uint16_t width = m_settings.spacing; +uint16_t tray_manager::calculate_w() const { // {{{ + uint16_t width = m_opts.spacing; for (auto&& client : m_clients) { if (client->mapped()) { - width += m_settings.spacing + m_settings.width; + width += m_opts.spacing + m_opts.width; } } @@ -767,34 +822,34 @@ uint16_t traymanager::calculate_w() const { // {{{ /** * Calculate height of tray window */ -uint16_t traymanager::calculate_h() const { // {{{ - return m_settings.height_fill; +uint16_t tray_manager::calculate_h() const { // {{{ + return m_opts.height_fill; } // }}} /** * Calculate x position of client window */ -int16_t traymanager::calculate_client_x(const xcb_window_t& win) { // {{{ +int16_t tray_manager::calculate_client_x(const xcb_window_t& win) { // {{{ for (size_t i = 0; i < m_clients.size(); i++) if (m_clients[i]->match(win)) - return m_settings.spacing + m_settings.width * i; - return m_settings.spacing; + return m_opts.spacing + m_opts.width * i; + return m_opts.spacing; } // }}} /** * Calculate y position of client window */ -int16_t traymanager::calculate_client_y() { // {{{ - return (m_settings.height_fill - m_settings.height) / 2; +int16_t tray_manager::calculate_client_y() { // {{{ + return (m_opts.height_fill - m_opts.height) / 2; } // }}} /** * Find tray client by window */ -shared_ptr traymanager::find_client(const xcb_window_t& win) const { // {{{ +shared_ptr tray_manager::find_client(const xcb_window_t& win) const { // {{{ for (auto&& client : m_clients) if (client->match(win)) { - return shared_ptr{client.get(), null_deleter{}}; + return shared_ptr{client.get(), null_deleter{}}; } return {}; } // }}} @@ -802,18 +857,18 @@ shared_ptr traymanager::find_client(const xcb_window_t& win) const { /** * Client error handling */ -void traymanager::remove_client(shared_ptr& client, bool reconfigure) { // {{{ +void tray_manager::remove_client(shared_ptr& client, bool reconfigure) { // {{{ m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client)); if (reconfigure) { - traymanager::reconfigure(); + tray_manager::reconfigure(); } } // }}} /** * Get number of mapped clients */ -int traymanager::mapped_clients() const { // {{{ +int tray_manager::mapped_clients() const { // {{{ int mapped_clients = 0; for (auto&& client : m_clients) { @@ -828,7 +883,7 @@ int traymanager::mapped_clients() const { // {{{ /** * Event callback : XCB_EXPOSE */ -void traymanager::handle(const evt::expose& evt) { // {{{ +void tray_manager::handle(const evt::expose& evt) { // {{{ if (m_activated && !m_clients.empty()) { m_log.trace("tray: Received expose event for %s", m_connection.id(evt->window)); reconfigure_window(); @@ -838,7 +893,7 @@ void traymanager::handle(const evt::expose& evt) { // {{{ /** * Event callback : XCB_VISIBILITY_NOTIFY */ -void traymanager::handle(const evt::visibility_notify& evt) { // {{{ +void tray_manager::handle(const evt::visibility_notify& evt) { // {{{ if (m_activated && !m_clients.empty()) { m_log.trace("tray: Received visibility_notify for %s", m_connection.id(evt->window)); reconfigure_window(); @@ -848,7 +903,7 @@ void traymanager::handle(const evt::visibility_notify& evt) { // {{{ /** * Event callback : XCB_CLIENT_MESSAGE */ -void traymanager::handle(const evt::client_message& evt) { // {{{ +void tray_manager::handle(const evt::client_message& evt) { // {{{ if (!m_activated) { return; } @@ -860,7 +915,7 @@ void traymanager::handle(const evt::client_message& evt) { // {{{ case SYSTEM_TRAY_REQUEST_DOCK: try { process_docking_request(evt->data.data32[2]); - } catch (const std::exception& err) { + } catch (const exception& err) { auto id = m_connection.id(evt->data.data32[2]); m_log.err("Error while processing docking request for %s (%s)", id, err.what()); } @@ -877,7 +932,7 @@ void traymanager::handle(const evt::client_message& evt) { // {{{ } else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW) { if (evt->window == m_tray) { m_log.warn("Received WM_DELETE"); - m_tray = XCB_NONE; + m_tray = 0; deactivate(); } } @@ -890,7 +945,7 @@ void traymanager::handle(const evt::client_message& evt) { // {{{ * wants to reconfigure its window. This is of course nothing we appreciate * so we return an answer that'll put him in place. */ -void traymanager::handle(const evt::configure_request& evt) { // {{{ +void tray_manager::handle(const evt::configure_request& evt) { // {{{ if (!m_activated) { return; } @@ -912,7 +967,7 @@ void traymanager::handle(const evt::configure_request& evt) { // {{{ /** * @see tray_manager::handle(const evt::configure_request&); */ -void traymanager::handle(const evt::resize_request& evt) { // {{{ +void tray_manager::handle(const evt::resize_request& evt) { // {{{ if (!m_activated) { return; } @@ -934,7 +989,7 @@ void traymanager::handle(const evt::resize_request& evt) { // {{{ /** * Event callback : XCB_SELECTION_CLEAR */ -void traymanager::handle(const evt::selection_clear& evt) { // {{{ +void tray_manager::handle(const evt::selection_clear& evt) { // {{{ if (!m_activated) { return; } else if (evt->selection != m_atom) { @@ -947,9 +1002,9 @@ void traymanager::handle(const evt::selection_clear& evt) { // {{{ m_log.warn("Lost systray selection, deactivating..."); m_othermanager = m_connection.get_selection_owner(m_atom)->owner; track_selection_owner(m_othermanager); - } catch (const std::exception& err) { + } catch (const exception& err) { m_log.err("Failed to get systray selection owner"); - m_othermanager = XCB_NONE; + m_othermanager = 0; } deactivate(); @@ -958,15 +1013,18 @@ void traymanager::handle(const evt::selection_clear& evt) { // {{{ /** * Event callback : XCB_PROPERTY_NOTIFY */ -void traymanager::handle(const evt::property_notify& evt) { // {{{ +void tray_manager::handle(const evt::property_notify& evt) { // {{{ if (!m_activated) { return; } else if (evt->atom == _XROOTMAP_ID) { reconfigure_bg(true); + refresh_window(); } else if (evt->atom == _XSETROOT_ID) { reconfigure_bg(true); + refresh_window(); } else if (evt->atom == ESETROOT_PMAP_ID) { reconfigure_bg(true); + refresh_window(); } else if (evt->atom != _XEMBED_INFO) { auto client = find_client(evt->window); if (!client) { @@ -995,7 +1053,7 @@ void traymanager::handle(const evt::property_notify& evt) { // {{{ /** * Event callback : XCB_REPARENT_NOTIFY */ -void traymanager::handle(const evt::reparent_notify& evt) { // {{{ +void tray_manager::handle(const evt::reparent_notify& evt) { // {{{ if (!m_activated) { return; } @@ -1010,7 +1068,7 @@ void traymanager::handle(const evt::reparent_notify& evt) { // {{{ /** * Event callback : XCB_DESTROY_NOTIFY */ -void traymanager::handle(const evt::destroy_notify& evt) { // {{{ +void tray_manager::handle(const evt::destroy_notify& evt) { // {{{ if (!m_activated && evt->window == m_othermanager) { m_log.trace("tray: Received destroy_notify"); m_log.info("Tray selection available... re-activating"); @@ -1028,7 +1086,7 @@ void traymanager::handle(const evt::destroy_notify& evt) { // {{{ /** * Event callback : XCB_MAP_NOTIFY */ -void traymanager::handle(const evt::map_notify& evt) { // {{{ +void tray_manager::handle(const evt::map_notify& evt) { // {{{ if (!m_activated) { return; } @@ -1049,7 +1107,7 @@ void traymanager::handle(const evt::map_notify& evt) { // {{{ } } - if (mapped_clients() > m_settings.configured_slots) { + if (mapped_clients() > m_opts.configured_slots) { reconfigure(); } } // }}} @@ -1057,7 +1115,7 @@ void traymanager::handle(const evt::map_notify& evt) { // {{{ /** * Event callback : XCB_UNMAP_NOTIFY */ -void traymanager::handle(const evt::unmap_notify& evt) { // {{{ +void tray_manager::handle(const evt::unmap_notify& evt) { // {{{ if (!m_activated) { return; } @@ -1069,8 +1127,8 @@ void traymanager::handle(const evt::unmap_notify& evt) { // {{{ } m_log.trace("tray: Update container mapped flag"); m_mapped = false; - m_settings.configured_w = 0; - m_settings.configured_x = 0; + m_opts.configured_w = 0; + m_opts.configured_x = 0; } else { auto client = find_client(evt->window); if (client) { diff --git a/src/x11/window.cpp b/src/x11/window.cpp index 5e466eb6..2c53eb16 100644 --- a/src/x11/window.cpp +++ b/src/x11/window.cpp @@ -1,23 +1,74 @@ +#include + #include "x11/window.hpp" #include "x11/xutils.hpp" LEMONBUDDY_NS -window window::create_checked( - int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* params) { - auto screen = connection().screen(); - auto visual = connection().visual_type(screen, 32).get(); - auto depth = (visual->visual_id == screen->root_visual) ? XCB_COPY_FROM_PARENT : 32; - uint32_t value_list[16]; - xutils::pack_values(mask, params, value_list); - connection().create_window_checked(depth, operator*(), screen->root, x, y, w, h, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, visual->visual_id, mask, value_list); +window window::create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h) { + if (*this == XCB_NONE) { + resource(connection(), connection().generate_id()); + } + + auto root = connection().screen()->root; + auto copy = XCB_COPY_FROM_PARENT; + connection().create_window_checked(copy, *this, root, x, y, w, h, 0, copy, copy, 0, nullptr); + return *this; } -window window::create_checked( - uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* params) { - return create_checked(0, 0, w, h, mask, params); +window window::create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p) { + if (*this == XCB_NONE) { + resource(connection(), connection().generate_id()); + } + + auto root = connection().screen()->root; + auto copy = XCB_COPY_FROM_PARENT; + uint32_t values[16]; + xutils::pack_values(mask, p, values); + connection().create_window_checked(copy, *this, root, x, y, w, h, 0, copy, copy, mask, values); + + return *this; +} + +window window::create_checked(uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p) { + return create_checked(0, 0, w, h, mask, p); +} + +window window::reconfigure_geom(uint16_t w, uint16_t h, int16_t x, int16_t y) { + uint32_t mask = 0; + uint32_t values[7]; + + xcb_params_configure_window_t params; + XCB_AUX_ADD_PARAM(&mask, ¶ms, width, w); + XCB_AUX_ADD_PARAM(&mask, ¶ms, height, h); + XCB_AUX_ADD_PARAM(&mask, ¶ms, x, x); + XCB_AUX_ADD_PARAM(&mask, ¶ms, y, y); + + xutils::pack_values(mask, ¶ms, values); + connection().configure_window_checked(*this, mask, values); + + return *this; +} + +window window::reconfigure_pos(int16_t x, int16_t y) { + uint32_t mask = 0; + uint32_t values[7]; + + xcb_params_configure_window_t params; + XCB_AUX_ADD_PARAM(&mask, ¶ms, x, x); + XCB_AUX_ADD_PARAM(&mask, ¶ms, y, y); + + xutils::pack_values(mask, ¶ms, values); + connection().configure_window_checked(*this, mask, values); + + return *this; +} + +void window::redraw() { + xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_FULLY_OBSCURED); + xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_UNOBSCURED); + connection().flush(); } LEMONBUDDY_NS_END