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);
}