From 3711e999ba64236670277b9a8d7f10533ec1fee9 Mon Sep 17 00:00:00 2001 From: patrick96 Date: Mon, 7 Mar 2022 15:12:42 +0100 Subject: [PATCH] Use individual wrapper window for each tray client --- include/components/logger.hpp | 16 +++--- include/x11/tray_client.hpp | 29 +++++++++-- include/x11/tray_manager.hpp | 2 +- include/x11/xembed.hpp | 50 +++++++++--------- src/components/bar.cpp | 6 +-- src/x11/tray_client.cpp | 97 +++++++++++++++++++++++++++-------- src/x11/tray_manager.cpp | 56 +++++++++++--------- src/x11/xembed.cpp | 4 +- 8 files changed, 174 insertions(+), 86 deletions(-) diff --git a/include/components/logger.hpp b/include/components/logger.hpp index 6d995072..cd59dbfb 100644 --- a/include/components/logger.hpp +++ b/include/components/logger.hpp @@ -34,11 +34,15 @@ class logger { explicit logger(loglevel level); + const logger& operator=(const logger&) const { + return *this; + } + static loglevel parse_verbosity(const string& name, loglevel fallback = loglevel::NONE); void verbosity(loglevel level); -#ifdef DEBUG_LOGGER // {{{ +#ifdef DEBUG_LOGGER // {{{ template void trace(const string& message, Args&&... args) const { output(loglevel::TRACE, message, std::forward(args)...); @@ -57,7 +61,7 @@ class logger { void trace(Args&&...) const {} template void trace_x(Args&&...) const {} -#endif // }}} +#endif // }}} /** * Output an info message @@ -118,21 +122,21 @@ class logger { return; } -#if defined(__clang__) // {{{ +#if defined(__clang__) // {{{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-security" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-security" -#endif // }}} +#endif // }}} dprintf(m_fd, (m_prefixes.at(level) + format + m_suffixes.at(level) + "\n").c_str(), convert(values)...); -#if defined(__clang__) // {{{ +#if defined(__clang__) // {{{ #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop -#endif // }}} +#endif // }}} } private: diff --git a/include/x11/tray_client.hpp b/include/x11/tray_client.hpp index a054c153..33a3807b 100644 --- a/include/x11/tray_client.hpp +++ b/include/x11/tray_client.hpp @@ -13,7 +13,7 @@ class connection; class tray_client : public non_copyable_mixin { public: - explicit tray_client(connection& conn, xcb_window_t win, size s); + explicit tray_client(const logger& log, connection& conn, xcb_window_t tray, xcb_window_t win, size s); ~tray_client(); tray_client(tray_client&&); @@ -27,7 +27,8 @@ class tray_client : public non_copyable_mixin { bool mapped() const; void mapped(bool state); - xcb_window_t window() const; + xcb_window_t embedder() const; + xcb_window_t client() const; void query_xembed(); bool is_xembed_supported() const; @@ -38,8 +39,27 @@ class tray_client : public non_copyable_mixin { void configure_notify(int x, int y) const; protected: + const logger& m_log; + connection& m_connection; - xcb_window_t m_window{XCB_NONE}; + + /** + * Embedder window. + * + * The docking client window is reparented to this window. + * This window is itself a child of the main tray window. + * + * This class owns this window and is responsible for creating/destroying it. + */ + xcb_window_t m_wrapper{XCB_NONE}; + + /** + * Client window. + * + * The window itself is owned by the application providing it. + * This class is responsible for correctly mapping and reparenting it in accordance with the XEMBED protocol. + */ + xcb_window_t m_client{XCB_NONE}; /** * Whether the client window supports XEMBED. @@ -50,9 +70,12 @@ class tray_client : public non_copyable_mixin { /** * _XEMBED_INFO of the client window + * + * Only valid if m_xembed_supported == true */ xembed::info m_xembed; + // TODO bool m_mapped{false}; size m_size; diff --git a/include/x11/tray_manager.hpp b/include/x11/tray_manager.hpp index cf5c5376..12b21688 100644 --- a/include/x11/tray_manager.hpp +++ b/include/x11/tray_manager.hpp @@ -69,7 +69,7 @@ struct tray_settings { /** * Number of clients currently mapped. */ - int num_clients{0}; + int num_mapped_clients{0}; // This is the width of the bar window // TODO directly read from bar_settings diff --git a/include/x11/xembed.hpp b/include/x11/xembed.hpp index 51407612..b663d74c 100644 --- a/include/x11/xembed.hpp +++ b/include/x11/xembed.hpp @@ -5,26 +5,26 @@ POLYBAR_NS -#define XEMBED_VERSION 0 -#define XEMBED_MAPPED (1 << 0) +static constexpr uint32_t XEMBED_VERSION = 0; +static constexpr uint32_t XEMBED_MAPPED = (1 << 0); -#define XEMBED_EMBEDDED_NOTIFY 0 -#define XEMBED_WINDOW_ACTIVATE 1 -#define XEMBED_WINDOW_DEACTIVATE 2 -#define XEMBED_REQUEST_FOCUS 3 -#define XEMBED_FOCUS_IN 3 -#define XEMBED_FOCUS_OUT 4 -#define XEMBED_FOCUS_NEXT 5 -#define XEMBED_FOCUS_PREV 6 +static constexpr uint32_t XEMBED_EMBEDDED_NOTIFY = 0; +static constexpr uint32_t XEMBED_WINDOW_ACTIVATE = 1; +static constexpr uint32_t XEMBED_WINDOW_DEACTIVATE = 2; +static constexpr uint32_t XEMBED_REQUEST_FOCUS = 3; +static constexpr uint32_t XEMBED_FOCUS_IN = 3; +static constexpr uint32_t XEMBED_FOCUS_OUT = 4; +static constexpr uint32_t XEMBED_FOCUS_NEXT = 5; +static constexpr uint32_t XEMBED_FOCUS_PREV = 6; -#define XEMBED_FOCUS_CURRENT 0 -#define XEMBED_FOCUS_FIRST 1 -#define XEMBED_FOCUS_LAST 1 +static constexpr uint32_t XEMBED_FOCUS_CURRENT = 0; +static constexpr uint32_t XEMBED_FOCUS_FIRST = 1; +static constexpr uint32_t XEMBED_FOCUS_LAST = 2; /** * Max XEMBED version supported. */ -#define XEMBED_MAX_VERSION UINT32_C(0) +static constexpr uint32_t XEMBED_MAX_VERSION = 0; /** * Implementation of parts of the XEMBED spec (as much as needed to get the tray working). @@ -34,28 +34,28 @@ POLYBAR_NS namespace xembed { class info { - public: - void set(uint32_t* data); + public: + void set(uint32_t* data); - uint32_t get_version() const; - uint32_t get_flags() const; + uint32_t get_version() const; + uint32_t get_flags() const; - bool is_mapped() const; + bool is_mapped() const; - protected: - uint32_t version; - uint32_t flags; + protected: + uint32_t version; + uint32_t flags; }; bool query(connection& conn, xcb_window_t win, info& data); - void send_message(connection& conn, xcb_window_t target, long message, long d1, long d2, long d3); + void send_message(connection& conn, xcb_window_t target, uint32_t message, uint32_t d1, uint32_t d2, uint32_t d3); void send_focus_event(connection& conn, xcb_window_t target); void notify_embedded(connection& conn, xcb_window_t win, xcb_window_t embedder, uint32_t version); void notify_activated(connection& conn, xcb_window_t win); void notify_deactivated(connection& conn, xcb_window_t win); - void notify_focused(connection& conn, xcb_window_t win, long focus_type); + void notify_focused(connection& conn, xcb_window_t win, uint32_t focus_type); void notify_unfocused(connection& conn, xcb_window_t win); void unembed(connection& conn, xcb_window_t win, xcb_window_t root); -} // namespace xembed +} // namespace xembed POLYBAR_NS_END diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 7a6cb86a..14074999 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -61,8 +61,8 @@ bar::make_type bar::make(loop& loop, bool only_initialize_values) { * TODO: Break out all tray handling */ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, loop& loop, - unique_ptr&& screen, unique_ptr&& dispatch, - unique_ptr&& action_ctxt, bool only_initialize_values) + unique_ptr&& screen, unique_ptr&& dispatch, unique_ptr&& action_ctxt, + bool only_initialize_values) : m_connection(conn) , m_sig(emitter) , m_conf(config) @@ -393,7 +393,7 @@ void bar::parse(string&& data, bool force) { auto rect = m_opts.inner_area(); - if (m_tray && !m_tray->settings().detached && m_tray->settings().num_clients > 0 && !m_tray->settings().adaptive) { + if (m_tray && !m_tray->settings().detached && m_tray->settings().num_mapped_clients > 0 && !m_tray->settings().adaptive) { auto trayalign = m_tray->settings().align; auto traywidth = m_tray->settings().win_size.w; if (trayalign == alignment::LEFT) { diff --git a/src/x11/tray_client.cpp b/src/x11/tray_client.cpp index 76945417..9a6c1ce5 100644 --- a/src/x11/tray_client.cpp +++ b/src/x11/tray_client.cpp @@ -5,24 +5,61 @@ #include "utils/memory.hpp" #include "x11/connection.hpp" +#include "x11/winspec.hpp" POLYBAR_NS -tray_client::tray_client(connection& conn, xcb_window_t win, size s) : m_connection(conn), m_window(win), m_size(s) {} +// TODO create wrapper window +tray_client::tray_client(const logger& log, connection& conn, xcb_window_t tray, xcb_window_t win, size s) + : m_log(log), m_connection(conn), m_client(win), m_size(s) { + auto geom = conn.get_geometry(win); + auto attrs = conn.get_window_attributes(win); + int depth = geom->depth; + xcb_visualid_t visual = attrs->visual; + m_log.trace("tray(%s): depth: %u, width: %u, height: %u, visual: 0x%x", conn.id(win), depth, geom->width, geom->height, visual); + + // clang-format off + m_wrapper = winspec(conn) + << cw_size(s.h, s.w) + << cw_pos(0, 0) + // TODO fix BadMatch error for redshift-gtk window + // << cw_depth(depth) + // << cw_visual(visual) + << cw_parent(tray) + // TODO add proper pixmap + << cw_params_back_pixmap(XCB_PIXMAP_NONE) + // << cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT) + << cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED) + << cw_params_event_mask(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT + | XCB_EVENT_MASK_PROPERTY_CHANGE + | XCB_EVENT_MASK_STRUCTURE_NOTIFY + | XCB_EVENT_MASK_EXPOSURE) + // << cw_flush(false); + // TODO Make unchecked + << cw_flush(true); + // clang-format on +} tray_client::~tray_client() { - if (m_window != XCB_NONE) { - xembed::unembed(m_connection, m_window, m_connection.root()); + if (m_client != XCB_NONE) { + xembed::unembed(m_connection, m_client, m_connection.root()); + } + + if (m_wrapper != XCB_NONE) { + m_connection.destroy_window(m_wrapper); } } -tray_client::tray_client(tray_client&& c) : m_connection(c.m_connection), m_size(c.m_size) { - std::swap(m_window, c.m_window); +tray_client::tray_client(tray_client&& c) : m_log(c.m_log), m_connection(c.m_connection), m_size(c.m_size) { + std::swap(m_wrapper, c.m_wrapper); + std::swap(m_client, c.m_client); } tray_client& tray_client::operator=(tray_client&& c) { + m_log = c.m_log; m_connection = c.m_connection; - std::swap(m_window, c.m_window); + std::swap(m_wrapper, c.m_wrapper); + std::swap(m_client, c.m_client); std::swap(m_size, c.m_size); return *this; } @@ -36,14 +73,14 @@ unsigned int tray_client::height() const { } void tray_client::clear_window() const { - m_connection.clear_area_checked(1, window(), 0, 0, width(), height()); + m_connection.clear_area_checked(1, client(), 0, 0, width(), height()); } /** - * Match given window against client window + * Is this the client for the given client window */ bool tray_client::match(const xcb_window_t& win) const { - return win == m_window; + return win == m_client; } /** @@ -60,15 +97,16 @@ void tray_client::mapped(bool state) { m_mapped = state; } -/** - * Get client window - */ -xcb_window_t tray_client::window() const { - return m_window; +xcb_window_t tray_client::embedder() const { + return m_wrapper; +} + +xcb_window_t tray_client::client() const { + return m_client; } void tray_client::query_xembed() { - m_xembed_supported = xembed::query(m_connection, m_window, m_xembed); + m_xembed_supported = xembed::query(m_connection, m_client, m_xembed); } bool tray_client::is_xembed_supported() const { @@ -83,6 +121,7 @@ const xembed::info& tray_client::get_xembed() const { * Make sure that the window mapping state is correct */ void tray_client::ensure_state() const { + // TODO correctly map/unmap wrapper bool should_be_mapped = true; if (is_xembed_supported()) { @@ -90,9 +129,11 @@ void tray_client::ensure_state() const { } if (!mapped() && should_be_mapped) { - m_connection.map_window_checked(window()); + m_connection.map_window_checked(embedder()); + m_connection.map_window_checked(client()); } else if (mapped() && !should_be_mapped) { - m_connection.unmap_window_checked(window()); + m_connection.unmap_window_checked(embedder()); + m_connection.unmap_window_checked(client()); } } @@ -100,6 +141,9 @@ void tray_client::ensure_state() const { * Configure window size */ void tray_client::reconfigure(int x, int y) const { + m_log.trace("tray(%s): moving to (%d, %d)", m_connection.id(client()), x, y); + + // TODO correctly reconfigure wrapper + client uint32_t configure_mask = 0; std::array configure_values{}; xcb_params_configure_window_t configure_params{}; @@ -109,17 +153,26 @@ void tray_client::reconfigure(int x, int y) const { XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, x, x); XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, y, y); connection::pack_values(configure_mask, &configure_params, configure_values); - m_connection.configure_window_checked(window(), configure_mask, configure_values.data()); + m_connection.configure_window_checked(embedder(), configure_mask, configure_values.data()); + + configure_mask = 0; + XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, width, m_size.w); + XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, height, m_size.h); + XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, x, 0); + 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()); } /** - * Respond to client resize requests + * Respond to client resize/move requests */ void tray_client::configure_notify(int x, int y) const { + // TODO remove x and y position. The position will always be (0,0) xcb_configure_notify_event_t notify; notify.response_type = XCB_CONFIGURE_NOTIFY; - notify.event = m_window; - notify.window = m_window; + notify.event = m_client; + notify.window = m_client; notify.override_redirect = false; notify.above_sibling = 0; notify.x = x; @@ -129,7 +182,7 @@ void tray_client::configure_notify(int x, int y) const { notify.border_width = 0; unsigned int mask{XCB_EVENT_MASK_STRUCTURE_NOTIFY}; - m_connection.send_event_checked(false, m_window, mask, reinterpret_cast(¬ify)); + m_connection.send_event_checked(false, m_client, mask, reinterpret_cast(¬ify)); } POLYBAR_NS_END diff --git a/src/x11/tray_manager.cpp b/src/x11/tray_manager.cpp index 7ba6f771..6dfc9800 100644 --- a/src/x11/tray_manager.cpp +++ b/src/x11/tray_manager.cpp @@ -265,7 +265,7 @@ void tray_manager::deactivate(bool clear_selection) { } m_opts.win_size.w = 0; - m_opts.num_clients = 0; + m_opts.num_mapped_clients = 0; m_acquired_selection = false; m_mapped = false; @@ -299,7 +299,7 @@ void tray_manager::reconfigure() { m_log.err("Failed to reconfigure tray background (%s)", err.what()); } - m_opts.num_clients = mapped_clients(); + m_opts.num_mapped_clients = mapped_clients(); guard.unlock(); refresh_window(); m_connection.flush(); @@ -432,8 +432,8 @@ void tray_manager::refresh_window() { client.clear_window(); } } catch (const std::exception& e) { - m_log.err("Failed to clear tray client %s '%s' (%s)", m_connection.id(client.window()), - ewmh_util::get_wm_name(client.window()), e.what()); + m_log.err("Failed to clear tray client %s '%s' (%s)", ewmh_util::get_wm_name(client.client()), + m_connection.id(client.client()), e.what()); } } @@ -480,7 +480,8 @@ void tray_manager::create_window() { << cw_params_event_mask(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |XCB_EVENT_MASK_STRUCTURE_NOTIFY |XCB_EVENT_MASK_EXPOSURE) - << cw_params_override_redirect(true) + // TODO + // << cw_params_override_redirect(true) << cw_parent(m_opts.bar_window); // clang-format on @@ -699,10 +700,7 @@ void tray_manager::track_selection_owner(xcb_window_t owner) { void tray_manager::process_docking_request(xcb_window_t win) { m_log.info("Processing docking request from '%s' (%s)", ewmh_util::get_wm_name(win), m_connection.id(win)); - tray_client client(m_connection, win, m_opts.client_size); - - auto geom = m_connection.get_geometry(win); - m_log.trace("tray: depth: %u, width: %u, height: %u", geom->depth, geom->width, geom->height); + tray_client client(m_log, m_connection, m_tray, win, m_opts.client_size); try { client.query_xembed(); @@ -722,29 +720,31 @@ void tray_manager::process_docking_request(xcb_window_t win) { const uint32_t value = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; m_log.trace("tray: Update client window"); - m_connection.change_window_attributes_checked(client.window(), mask, &value); + m_connection.change_window_attributes_checked(client.client(), mask, &value); m_log.trace("tray: Configure client size"); client.reconfigure(0, 0); - // TODO properly support tray icon backgrounds - auto p = XCB_BACK_PIXMAP_NONE; - m_connection.change_window_attributes_checked(client.window(), XCB_CW_BACK_PIXMAP, &p); + // TODO put this into tray_client class m_log.trace("tray: Reparent client"); m_connection.reparent_window_checked( - client.window(), m_tray, calculate_client_x(client.window()), calculate_client_y()); + client.client(), client.embedder(), calculate_client_x(client.client()), calculate_client_y()); m_log.trace("tray: Add client window to the save set"); - m_connection.change_save_set_checked(XCB_SET_MODE_INSERT, client.window()); + // TODO move this into tray_client + m_connection.change_save_set_checked(XCB_SET_MODE_INSERT, client.client()); if (client.is_xembed_supported()) { m_log.trace("tray: Send embbeded notification to client"); - xembed::notify_embedded(m_connection, client.window(), m_tray, client.get_xembed().get_version()); + // TODO move this into tray_client + xembed::notify_embedded(m_connection, client.client(), client.embedder(), client.get_xembed().get_version()); } if (!client.is_xembed_supported() || client.get_xembed().is_mapped()) { m_log.trace("tray: Map client"); - m_connection.map_window_checked(client.window()); + // TODO move this into tray_client + m_connection.map_window_checked(client.client()); + m_connection.map_window_checked(client.embedder()); } } catch (const std::exception& err) { m_log.err("Failed to setup tray client '%s' (%s) removing... (%s)", ewmh_util::get_wm_name(win), @@ -840,7 +840,7 @@ tray_client* tray_manager::find_client(const xcb_window_t& win) { * Remove tray client */ void tray_manager::remove_client(const tray_client& client, bool reconfigure) { - remove_client(client.window(), reconfigure); + remove_client(client.client(), reconfigure); } /** @@ -993,8 +993,6 @@ void tray_manager::handle(const evt::property_notify& evt) { m_log.trace("tray: _XEMBED_INFO: %s", m_connection.id(evt->window)); - auto win = client->window(); - if (evt->state == XCB_PROPERTY_NEW_VALUE) { m_log.trace("tray: _XEMBED_INFO value has changed"); } @@ -1003,7 +1001,7 @@ void tray_manager::handle(const evt::property_notify& evt) { client->query_xembed(); } catch (const xpp::x::error::window& err) { m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what()); - remove_client(win, true); + remove_client(*client, true); return; } @@ -1019,9 +1017,19 @@ void tray_manager::handle(const evt::property_notify& evt) { * Event callback : XCB_REPARENT_NOTIFY */ void tray_manager::handle(const evt::reparent_notify& evt) { - if (m_activated && is_embedded(evt->window) && evt->parent != m_tray) { + if (!m_activated) { + return; + } + + auto client = find_client(evt->window); + + if (!client) { + return; + } + + if (evt->parent != client->embedder()) { m_log.trace("tray: Received reparent_notify for client, remove..."); - remove_client(evt->window); + remove_client(*client); } } @@ -1054,7 +1062,7 @@ void tray_manager::handle(const evt::map_notify& evt) { m_log.trace("tray: Received map_notify"); m_log.trace("tray: Set client mapped"); find_client(evt->window)->mapped(true); - if (mapped_clients() > m_opts.num_clients) { + if (mapped_clients() > m_opts.num_mapped_clients) { reconfigure(); } } diff --git a/src/x11/xembed.cpp b/src/x11/xembed.cpp index c6b8a67d..3b490d8e 100644 --- a/src/x11/xembed.cpp +++ b/src/x11/xembed.cpp @@ -48,7 +48,7 @@ namespace xembed { /** * Send _XEMBED messages */ - void send_message(connection& conn, xcb_window_t target, long message, long d1, long d2, long d3) { + void send_message(connection& conn, xcb_window_t target, uint32_t message, uint32_t d1, uint32_t d2, uint32_t d3) { auto msg = conn.make_client_message(_XEMBED, target); msg.data.data32[0] = XCB_CURRENT_TIME; msg.data.data32[1] = message; @@ -92,7 +92,7 @@ namespace xembed { /** * Send window focused notification */ - void notify_focused(connection& conn, xcb_window_t win, long focus_type) { + void notify_focused(connection& conn, xcb_window_t win, uint32_t focus_type) { send_message(conn, win, XEMBED_FOCUS_IN, focus_type, 0, 0); }