diff --git a/include/components/types.hpp b/include/components/types.hpp index 9c152654..3f8b9831 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -82,6 +82,10 @@ enum class strut { struct position { int x{0}; int y{0}; + + bool operator==(const position& other) { + return this->x == other.x && this->y == other.y; + } }; struct size { diff --git a/include/x11/tray_client.hpp b/include/x11/tray_client.hpp index eb1a9189..0edd37a1 100644 --- a/include/x11/tray_client.hpp +++ b/include/x11/tray_client.hpp @@ -2,8 +2,11 @@ #include +#include "cairo/context.hpp" +#include "cairo/surface.hpp" #include "common.hpp" #include "utils/concurrency.hpp" +#include "x11/background_manager.hpp" #include "x11/xembed.hpp" /* @@ -19,7 +22,8 @@ class connection; class tray_client : public non_copyable_mixin, public non_movable_mixin { public: - explicit tray_client(const logger& log, connection& conn, xcb_window_t tray, xcb_window_t win, size s); + explicit tray_client( + const logger& log, connection& conn, xcb_window_t parent, xcb_window_t win, size s, uint32_t desired_background); ~tray_client(); string name() const; @@ -51,9 +55,14 @@ class tray_client : public non_copyable_mixin, public non_movable_mixin { void add_to_save_set() const; void ensure_state() const; - void reconfigure(int x, int y) const; + void set_position(int x, int y); void configure_notify() const; + void update_bg() const; + + protected: + void observe_background(); + protected: const logger& m_log; @@ -107,6 +116,13 @@ class tray_client : public non_copyable_mixin, public non_movable_mixin { bool m_hidden{false}; size m_size; + position m_pos{0, 0}; + + rgba m_desired_background; + background_manager& m_background_manager; + shared_ptr m_bg_slice; + unique_ptr m_context; + unique_ptr m_surface; }; POLYBAR_NS_END diff --git a/include/x11/tray_manager.hpp b/include/x11/tray_manager.hpp index 4b27fd41..be453da4 100644 --- a/include/x11/tray_manager.hpp +++ b/include/x11/tray_manager.hpp @@ -86,8 +86,7 @@ class tray_manager : public xpp::event::sinkdepth; @@ -37,16 +44,13 @@ tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray, */ // clang-format off m_wrapper = winspec(conn) - << cw_size(s.h, s.w) + << cw_size(s.w, s.h) << cw_pos(0, 0) << cw_depth(client_depth) << cw_visual(client_visual) - << cw_parent(tray) + << cw_parent(parent) << cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT) - // TODO add proper pixmap - << cw_params_back_pixmap(XCB_PIXMAP_NONE) - // << cw_params_back_pixel(0x00ff00) - // The X server requires the border pixel to be defined if the depth doesn't match the parent window + // The X server requires the border pixel to be defined if the depth doesn't match the parent (bar) window << cw_params_border_pixel(conn.screen()->black_pixel) << cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED) << cw_params_save_under(true) @@ -57,6 +61,50 @@ tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray, << cw_params_colormap(client_colormap) << cw_flush(true); // clang-format on + + // TODO destroy in destructor + xcb_pixmap_t pixmap = m_connection.generate_id(); + + try { + m_connection.create_pixmap_checked(client_depth, pixmap, m_wrapper, s.w, s.h); + } catch (const std::exception& err) { + // TODO in case of error, fall back to desired_background + m_log.err("Failed to create pixmap for tray background (err: %s)", err.what()); + throw; + } + + try { + m_connection.change_window_attributes_checked(m_wrapper, XCB_CW_BACK_PIXMAP, &pixmap); + } catch (const std::exception& err) { + // TODO in case of error, fall back to desired_background + m_log.err("Failed to set tray window back pixmap (%s)", err.what()); + throw; + } + + // TODO destroy in destructor + xcb_gcontext_t gc = m_connection.generate_id(); + try { + xcb_params_gc_t params{}; + uint32_t mask = 0; + XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, 1); + std::array values; + connection::pack_values(mask, ¶ms, values); + m_connection.create_gc_checked(gc, pixmap, mask, values.data()); + } catch (const std::exception& err) { + m_log.err("Failed to create gcontext for tray background (err: %s)", err.what()); + throw; + } + + xcb_visualtype_t* visual = m_connection.visual_type_for_id(client_visual); + if (!visual) { + // TODO in case of error, fall back to desired_background + throw std::runtime_error("Failed to get root visual for tray background"); + } + + m_surface = make_unique(m_connection, pixmap, visual, s.w, s.h); + m_context = make_unique(*m_surface, m_log); + + observe_background(); } tray_client::~tray_client() { @@ -94,11 +142,6 @@ void tray_client::update_client_attributes() const { XCB_AUX_ADD_PARAM( &configure_mask, &configure_params, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY); - // TODO - XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, back_pixel, 0xff0000ff); - // TODO - XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, back_pixmap, XCB_PIXMAP_NONE); - connection::pack_values(configure_mask, &configure_params, configure_values); m_log.trace("%s: Update client window", name()); @@ -220,13 +263,21 @@ void tray_client::ensure_state() const { } /** - * Configure window size + * Configure window position */ -void tray_client::reconfigure(int x, int y) const { +void tray_client::set_position(int x, int y) { m_log.trace("%s: moving to (%d, %d)", name(), x, y); + position new_pos{x, y}; + + if (new_pos == m_pos) { + return; + } + + m_pos = new_pos; + uint32_t configure_mask = 0; - std::array configure_values{}; + array configure_values{}; xcb_params_configure_window_t configure_params{}; XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, width, m_size.w); @@ -243,6 +294,9 @@ void tray_client::reconfigure(int x, int y) const { XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, y, 0); connection::pack_values(configure_mask, &configure_params, configure_values); m_connection.configure_window_checked(client(), configure_mask, configure_values.data()); + + // The position has changed, we need a new background slice. + observe_background(); } /** @@ -265,4 +319,30 @@ void tray_client::configure_notify() const { m_connection.send_event_checked(false, client(), mask, reinterpret_cast(¬ify)); } +/** + * Redraw background using the observed background slice. + */ +void tray_client::update_bg() const { + m_log.trace("%s: Update background", name()); + + // Composite background slice with background color. + m_context->clear(); + *m_context << CAIRO_OPERATOR_SOURCE << *m_bg_slice->get_surface(); + m_context->paint(); + *m_context << CAIRO_OPERATOR_OVER << m_desired_background; + m_context->paint(); + + m_surface->flush(); + + clear_window(); + m_connection.flush(); +} + +void tray_client::observe_background() { + xcb_rectangle_t rect{0, 0, static_cast(m_size.w), static_cast(m_size.h)}; + m_bg_slice = m_background_manager.observe(rect, m_wrapper); + + update_bg(); +} + POLYBAR_NS_END diff --git a/src/x11/tray_manager.cpp b/src/x11/tray_manager.cpp index 0c7f94e6..7d1f5b3d 100644 --- a/src/x11/tray_manager.cpp +++ b/src/x11/tray_manager.cpp @@ -132,7 +132,7 @@ void tray_manager::activate() { try { set_tray_colors(); set_tray_orientation(); - set_tray_visual(); + // set_tray_visual(); } catch (const exception& err) { m_log.err(err.what()); m_log.err("Cannot activate tray manager... failed to setup window"); @@ -278,7 +278,7 @@ void tray_manager::reconfigure_clients() { client->ensure_state(); if (client->mapped()) { - client->reconfigure(x, calculate_client_y()); + client->set_position(x, calculate_client_y()); } x += m_opts.client_size.w + m_opts.spacing; @@ -295,9 +295,9 @@ void tray_manager::reconfigure_clients() { } /** - * Refresh the bar window by clearing it along with each client window + * Redraw client windows. */ -void tray_manager::refresh_window() { +void tray_manager::redraw_clients() { if (!is_visible()) { return; } @@ -307,7 +307,7 @@ void tray_manager::refresh_window() { for (auto& client : m_clients) { try { if (client->mapped()) { - client->clear_window(); + client->update_bg(); } } catch (const std::exception& e) { m_log.err("tray: Failed to clear %s (%s)", client->name(), e.what()); @@ -317,15 +317,6 @@ void tray_manager::refresh_window() { m_connection.flush(); } -/** - * Redraw window - * - * TODO better name - */ -void tray_manager::redraw_window() { - refresh_window(); -} - /** * Find the systray selection atom */ @@ -371,6 +362,7 @@ void tray_manager::set_tray_orientation() { XCB_ATOM_CARDINAL, 32, 1, &orientation); } +// TODO remove void tray_manager::set_tray_visual() { // TODO use bar visual const uint32_t visualid = m_connection.visual_type(XCB_VISUAL_CLASS_TRUE_COLOR, 32)->visual_id; @@ -452,7 +444,8 @@ void tray_manager::process_docking_request(xcb_window_t win) { m_log.info("tray: Processing docking request from '%s' (%s)", ewmh_util::get_wm_name(win), m_connection.id(win)); try { - auto client = make_unique(m_log, m_connection, m_opts.selection_owner, win, m_opts.client_size); + auto client = make_unique( + m_log, m_connection, m_opts.selection_owner, win, m_opts.client_size, m_bar_opts.background.value()); try { client->query_xembed(); @@ -578,7 +571,7 @@ bool tray_manager::change_visibility(bool visible) { } if (!m_hidden) { - redraw_window(); + redraw_clients(); } m_connection.flush(); @@ -591,7 +584,7 @@ bool tray_manager::change_visibility(bool visible) { */ void tray_manager::handle(const evt::expose& evt) { if (is_active() && !m_clients.empty() && evt->count == 0) { - redraw_window(); + redraw_clients(); } } @@ -740,7 +733,7 @@ void tray_manager::handle(const evt::destroy_notify& evt) { } else if (is_active() && is_embedded(evt->window)) { m_log.info("tray: Received destroy_notify for client, remove..."); remove_client(evt->window); - redraw_window(); + redraw_clients(); } } @@ -750,7 +743,7 @@ void tray_manager::handle(const evt::destroy_notify& evt) { void tray_manager::handle(const evt::map_notify& evt) { if (is_active() && evt->window == m_opts.selection_owner) { m_log.trace("tray: Received map_notify for selection owner"); - redraw_window(); + redraw_clients(); } else if (is_embedded(evt->window)) { auto client = find_client(evt->window); @@ -789,9 +782,8 @@ void tray_manager::handle(const evt::unmap_notify& evt) { } } -// TODO maybe remove signal bool tray_manager::on(const signals::ui::update_background&) { - redraw_window(); + redraw_clients(); return false; }