From 4f9f07eefd9f00126534c0071441f1f288c5fe11 Mon Sep 17 00:00:00 2001
From: patrick96
Date: Mon, 15 May 2023 14:00:00 +0200
Subject: [PATCH] restack: Add ewmh restacking strategy
Positions the bar window above the _NET_SUPPORTING_WM_CHECK window
The generic restacking strategy now first tries the ewmh strategy, the
the bottom strategy.
---
include/utils/restack.hpp | 2 +
include/x11/ewmh.hpp | 3 +
src/components/bar.cpp | 6 +-
src/utils/restack.cpp | 50 ++++-
src/x11/ewmh.cpp | 373 +++++++++++++++++++++-----------------
5 files changed, 258 insertions(+), 176 deletions(-)
diff --git a/include/utils/restack.hpp b/include/utils/restack.hpp
index 7fdb98a8..f7ec9b6c 100644
--- a/include/utils/restack.hpp
+++ b/include/utils/restack.hpp
@@ -11,8 +11,10 @@ namespace restack_util {
using params = std::pair;
void restack_relative(connection& conn, xcb_window_t win, xcb_window_t sibling, xcb_stack_mode_t stack_mode);
+ string stack_mode_to_string(xcb_stack_mode_t mode);
bool are_siblings(connection& conn, xcb_window_t win, xcb_window_t sibling);
params get_bottom_params(connection& conn, xcb_window_t bar_window);
+ params get_ewmh_params(connection& conn);
params get_generic_params(connection& conn, xcb_window_t bar_window);
}
diff --git a/include/x11/ewmh.hpp b/include/x11/ewmh.hpp
index 3dafa70a..a349a596 100644
--- a/include/x11/ewmh.hpp
+++ b/include/x11/ewmh.hpp
@@ -53,6 +53,9 @@ namespace ewmh_util {
void set_wm_window_opacity(xcb_window_t win, unsigned long int values);
vector get_client_list(int screen = 0);
+
+ xcb_window_t get_supporting_wm_check(xcb_window_t win);
+ xcb_window_t get_ewmh_meta_window(xcb_window_t root);
} // namespace ewmh_util
POLYBAR_NS_END
diff --git a/src/components/bar.cpp b/src/components/bar.cpp
index 22c730ba..4ae12a49 100644
--- a/src/components/bar.cpp
+++ b/src/components/bar.cpp
@@ -501,7 +501,9 @@ void bar::restack_window() {
xcb_stack_mode_t stack_mode = XCB_STACK_MODE_ABOVE;
if (wm_restack == "generic") {
- std::tie(restack_sibling, stack_mode) = restack_util::get_bottom_params(m_connection, bar_window);
+ std::tie(restack_sibling, stack_mode) = restack_util::get_generic_params(m_connection, bar_window);
+ } else if (wm_restack == "ewmh") {
+ std::tie(restack_sibling, stack_mode) = restack_util::get_ewmh_params(m_connection);
} else if (wm_restack == "bottom") {
std::tie(restack_sibling, stack_mode) = restack_util::get_bottom_params(m_connection, bar_window);
} else if (wm_restack == "bspwm") {
@@ -520,6 +522,8 @@ void bar::restack_window() {
if (restack_sibling != XCB_NONE) {
try {
+ m_log.info("bar: Restacking bar window relative to %s with stacking mode %s", m_connection.id(restack_sibling),
+ restack_util::stack_mode_to_string(stack_mode));
restack_util::restack_relative(m_connection, bar_window, restack_sibling, stack_mode);
m_log.info("Successfully restacked bar window");
} catch (const exception& err) {
diff --git a/src/utils/restack.cpp b/src/utils/restack.cpp
index 7f4962a4..b65f47b8 100644
--- a/src/utils/restack.cpp
+++ b/src/utils/restack.cpp
@@ -22,6 +22,23 @@ void restack_relative(connection& conn, xcb_window_t win, xcb_window_t sibling,
conn.configure_window_checked(win, value_mask, value_list.data());
}
+string stack_mode_to_string(xcb_stack_mode_t mode) {
+ switch (mode) {
+ case XCB_STACK_MODE_ABOVE:
+ return "ABOVE";
+ case XCB_STACK_MODE_BELOW:
+ return "BELOW";
+ case XCB_STACK_MODE_TOP_IF:
+ return "TOP_IF";
+ case XCB_STACK_MODE_BOTTOM_IF:
+ return "BOTTOM_IF";
+ case XCB_STACK_MODE_OPPOSITE:
+ return "OPPOSITE";
+ }
+
+ return "UNKNOWN";
+}
+
/**
* @return true iff the two given windows are sibings (are different windows and have same parent).
*/
@@ -40,7 +57,7 @@ bool are_siblings(connection& conn, xcb_window_t win, xcb_window_t sibling) {
*
* Moves the bar window to the bottom of the window stack
*/
-std::pair get_bottom_params(connection& conn, xcb_window_t bar_window) {
+params get_bottom_params(connection& conn, xcb_window_t bar_window) {
auto children = conn.query_tree(conn.root()).children();
if (children.begin() != children.end() && *children.begin() != bar_window) {
return {*children.begin(), XCB_STACK_MODE_BELOW};
@@ -49,19 +66,42 @@ std::pair get_bottom_params(connection& conn, xc
return NONE_PARAMS;
}
+/**
+ * EWMH restack strategy.
+ *
+ * Moves the bar window above the WM meta window (_NET_SUPPORTING_WM_CHECK).
+ * This window is generally towards the bottom of the window stack, but still above other windows that could interfere.
+ *
+ * @see ewmh_util::get_ewmh_meta_window
+ */
+params get_ewmh_params(connection& conn) {
+ if (auto meta_window = ewmh_util::get_ewmh_meta_window(conn.root())) {
+ return {meta_window, XCB_STACK_MODE_ABOVE};
+ }
+
+ return NONE_PARAMS;
+}
+
/**
* Generic restack stratgey.
*
* Tries to provide the best WM-agnostic restacking.
*
* Currently tries to the following stratgies in order:
+ * * ewmh
* * bottom
*/
-std::pair get_generic_params(connection& conn, xcb_window_t bar_window) {
- auto [sibling, mode] = get_bottom_params(conn, bar_window);
+params get_generic_params(connection& conn, xcb_window_t bar_window) {
+ auto ewmh_params = get_ewmh_params(conn);
- if (are_siblings(conn, bar_window, sibling)) {
- return {sibling, mode};
+ if (are_siblings(conn, bar_window, ewmh_params.first)) {
+ return ewmh_params;
+ }
+
+ auto bottom_params = get_bottom_params(conn, bar_window);
+
+ if (are_siblings(conn, bar_window, bottom_params.first)) {
+ return bottom_params;
}
return NONE_PARAMS;
diff --git a/src/x11/ewmh.cpp b/src/x11/ewmh.cpp
index e7b488d9..4eb9183a 100644
--- a/src/x11/ewmh.cpp
+++ b/src/x11/ewmh.cpp
@@ -11,188 +11,221 @@ POLYBAR_NS
namespace ewmh_util {
- ewmh_connection::ewmh_connection() {
- xcb_ewmh_init_atoms_replies(&c, xcb_ewmh_init_atoms(connection::make(), &c), nullptr);
- }
+ewmh_connection::ewmh_connection() {
+ xcb_ewmh_init_atoms_replies(&c, xcb_ewmh_init_atoms(connection::make(), &c), nullptr);
+}
- ewmh_connection::~ewmh_connection() {
- xcb_ewmh_connection_wipe(&c);
- }
+ewmh_connection::~ewmh_connection() {
+ xcb_ewmh_connection_wipe(&c);
+}
- xcb_ewmh_connection_t* ewmh_connection::operator->() {
- return &c;
- }
+xcb_ewmh_connection_t* ewmh_connection::operator->() {
+ return &c;
+}
- ewmh_connection::operator xcb_ewmh_connection_t*() {
- return &c;
- }
+ewmh_connection::operator xcb_ewmh_connection_t*() {
+ return &c;
+}
- ewmh_connection& initialize() {
- static ewmh_connection c;
- return c;
- }
+ewmh_connection& initialize() {
+ static ewmh_connection c;
+ return c;
+}
- bool supports(xcb_atom_t atom, int screen) {
- auto& conn = initialize();
- xcb_ewmh_get_atoms_reply_t reply{};
- if (xcb_ewmh_get_supported_reply(conn, xcb_ewmh_get_supported(conn, screen), &reply, nullptr)) {
- for (size_t n = 0; n < reply.atoms_len; n++) {
- if (reply.atoms[n] == atom) {
- xcb_ewmh_get_atoms_reply_wipe(&reply);
- return true;
- }
- }
- xcb_ewmh_get_atoms_reply_wipe(&reply);
- }
- return false;
- }
-
- string get_wm_name(xcb_window_t win) {
- auto& conn = initialize();
- xcb_ewmh_get_utf8_strings_reply_t utf8_reply{};
- if (xcb_ewmh_get_wm_name_reply(conn, xcb_ewmh_get_wm_name(conn, win), &utf8_reply, nullptr)) {
- return get_reply_string(&utf8_reply);
- }
- return "";
- }
-
- string get_visible_name(xcb_window_t win) {
- auto& conn = initialize();
- xcb_ewmh_get_utf8_strings_reply_t utf8_reply{};
- if (xcb_ewmh_get_wm_visible_name_reply(conn, xcb_ewmh_get_wm_visible_name(conn, win), &utf8_reply, nullptr)) {
- return get_reply_string(&utf8_reply);
- }
- return "";
- }
-
- string get_icon_name(xcb_window_t win) {
- auto& conn = initialize();
- xcb_ewmh_get_utf8_strings_reply_t utf8_reply{};
- if (xcb_ewmh_get_wm_icon_name_reply(conn, xcb_ewmh_get_wm_icon_name(conn, win), &utf8_reply, nullptr)) {
- return get_reply_string(&utf8_reply);
- }
- return "";
- }
-
- string get_reply_string(xcb_ewmh_get_utf8_strings_reply_t* reply) {
- string str;
- if (reply) {
- str = string(reply->strings, reply->strings_len);
- xcb_ewmh_get_utf8_strings_reply_wipe(reply);
- }
- return str;
- }
-
- unsigned int get_current_desktop(int screen) {
- auto& conn = initialize();
- unsigned int desktop = XCB_NONE;
- xcb_ewmh_get_current_desktop_reply(conn, xcb_ewmh_get_current_desktop(conn, screen), &desktop, nullptr);
- return desktop;
- }
-
- unsigned int get_number_of_desktops(int screen) {
- auto& conn = initialize();
- unsigned int desktops = XCB_NONE;
- xcb_ewmh_get_number_of_desktops_reply(conn, xcb_ewmh_get_number_of_desktops(conn, screen), &desktops, nullptr);
- return desktops;
- }
-
- vector get_desktop_viewports(int screen) {
- auto& conn = initialize();
- vector viewports;
- xcb_ewmh_get_desktop_viewport_reply_t reply{};
- if (xcb_ewmh_get_desktop_viewport_reply(conn, xcb_ewmh_get_desktop_viewport(conn, screen), &reply, nullptr)) {
- for (size_t n = 0; n < reply.desktop_viewport_len; n++) {
- viewports.emplace_back(position{
- static_cast(reply.desktop_viewport[n].x), static_cast(reply.desktop_viewport[n].y)});
+bool supports(xcb_atom_t atom, int screen) {
+ auto& conn = initialize();
+ xcb_ewmh_get_atoms_reply_t reply{};
+ if (xcb_ewmh_get_supported_reply(conn, xcb_ewmh_get_supported(conn, screen), &reply, nullptr)) {
+ for (size_t n = 0; n < reply.atoms_len; n++) {
+ if (reply.atoms[n] == atom) {
+ xcb_ewmh_get_atoms_reply_wipe(&reply);
+ return true;
}
}
- return viewports;
+ xcb_ewmh_get_atoms_reply_wipe(&reply);
}
+ return false;
+}
- vector get_desktop_names(int screen) {
- auto& conn = initialize();
- xcb_ewmh_get_utf8_strings_reply_t reply{};
- if (xcb_ewmh_get_desktop_names_reply(conn, xcb_ewmh_get_desktop_names(conn, screen), &reply, nullptr)) {
- return string_util::split(string(reply.strings, reply.strings_len), '\0');
+string get_wm_name(xcb_window_t win) {
+ auto& conn = initialize();
+ xcb_ewmh_get_utf8_strings_reply_t utf8_reply{};
+ if (xcb_ewmh_get_wm_name_reply(conn, xcb_ewmh_get_wm_name(conn, win), &utf8_reply, nullptr)) {
+ return get_reply_string(&utf8_reply);
+ }
+ return "";
+}
+
+string get_visible_name(xcb_window_t win) {
+ auto& conn = initialize();
+ xcb_ewmh_get_utf8_strings_reply_t utf8_reply{};
+ if (xcb_ewmh_get_wm_visible_name_reply(conn, xcb_ewmh_get_wm_visible_name(conn, win), &utf8_reply, nullptr)) {
+ return get_reply_string(&utf8_reply);
+ }
+ return "";
+}
+
+string get_icon_name(xcb_window_t win) {
+ auto& conn = initialize();
+ xcb_ewmh_get_utf8_strings_reply_t utf8_reply{};
+ if (xcb_ewmh_get_wm_icon_name_reply(conn, xcb_ewmh_get_wm_icon_name(conn, win), &utf8_reply, nullptr)) {
+ return get_reply_string(&utf8_reply);
+ }
+ return "";
+}
+
+string get_reply_string(xcb_ewmh_get_utf8_strings_reply_t* reply) {
+ string str;
+ if (reply) {
+ str = string(reply->strings, reply->strings_len);
+ xcb_ewmh_get_utf8_strings_reply_wipe(reply);
+ }
+ return str;
+}
+
+unsigned int get_current_desktop(int screen) {
+ auto& conn = initialize();
+ unsigned int desktop = XCB_NONE;
+ xcb_ewmh_get_current_desktop_reply(conn, xcb_ewmh_get_current_desktop(conn, screen), &desktop, nullptr);
+ return desktop;
+}
+
+unsigned int get_number_of_desktops(int screen) {
+ auto& conn = initialize();
+ unsigned int desktops = XCB_NONE;
+ xcb_ewmh_get_number_of_desktops_reply(conn, xcb_ewmh_get_number_of_desktops(conn, screen), &desktops, nullptr);
+ return desktops;
+}
+
+vector get_desktop_viewports(int screen) {
+ auto& conn = initialize();
+ vector viewports;
+ xcb_ewmh_get_desktop_viewport_reply_t reply{};
+ if (xcb_ewmh_get_desktop_viewport_reply(conn, xcb_ewmh_get_desktop_viewport(conn, screen), &reply, nullptr)) {
+ for (size_t n = 0; n < reply.desktop_viewport_len; n++) {
+ viewports.emplace_back(position{
+ static_cast(reply.desktop_viewport[n].x), static_cast(reply.desktop_viewport[n].y)});
}
- return {};
+ }
+ return viewports;
+}
+
+vector get_desktop_names(int screen) {
+ auto& conn = initialize();
+ xcb_ewmh_get_utf8_strings_reply_t reply{};
+ if (xcb_ewmh_get_desktop_names_reply(conn, xcb_ewmh_get_desktop_names(conn, screen), &reply, nullptr)) {
+ return string_util::split(string(reply.strings, reply.strings_len), '\0');
+ }
+ return {};
+}
+
+xcb_window_t get_active_window(int screen) {
+ auto& conn = initialize();
+ unsigned int win = XCB_NONE;
+ xcb_ewmh_get_active_window_reply(conn, xcb_ewmh_get_active_window(conn, screen), &win, nullptr);
+ return win;
+}
+
+void change_current_desktop(unsigned int desktop) {
+ auto& conn = initialize();
+ xcb_ewmh_request_change_current_desktop(conn, 0, desktop, XCB_CURRENT_TIME);
+ xcb_flush(conn->connection);
+}
+
+unsigned int get_desktop_from_window(xcb_window_t window) {
+ auto& conn = initialize();
+ unsigned int desktop = XCB_NONE;
+ xcb_ewmh_get_wm_desktop_reply(conn, xcb_ewmh_get_wm_desktop(conn, window), &desktop, nullptr);
+ return desktop;
+}
+
+void set_wm_window_type(xcb_window_t win, vector types) {
+ auto& conn = initialize();
+ xcb_ewmh_set_wm_window_type(conn, win, types.size(), types.data());
+ xcb_flush(conn->connection);
+}
+
+void set_wm_state(xcb_window_t win, vector states) {
+ auto& conn = initialize();
+ xcb_ewmh_set_wm_state(conn, win, states.size(), states.data());
+ xcb_flush(conn->connection);
+}
+
+vector get_wm_state(xcb_window_t win) {
+ auto& conn = initialize();
+ xcb_ewmh_get_atoms_reply_t reply;
+ if (xcb_ewmh_get_wm_state_reply(conn, xcb_ewmh_get_wm_state(conn, win), &reply, nullptr)) {
+ return {reply.atoms, reply.atoms + reply.atoms_len};
+ }
+ return {};
+}
+
+void set_wm_pid(xcb_window_t win) {
+ auto& conn = initialize();
+ xcb_ewmh_set_wm_pid(conn, win, getpid());
+ xcb_flush(conn->connection);
+}
+
+void set_wm_pid(xcb_window_t win, unsigned int pid) {
+ auto& conn = initialize();
+ xcb_ewmh_set_wm_pid(conn, win, pid);
+ xcb_flush(conn->connection);
+}
+
+void set_wm_desktop(xcb_window_t win, unsigned int desktop) {
+ auto& conn = initialize();
+ xcb_ewmh_set_wm_desktop(conn, win, desktop);
+ xcb_flush(conn->connection);
+}
+
+void set_wm_window_opacity(xcb_window_t win, unsigned long int values) {
+ auto& conn = initialize();
+ xcb_change_property(
+ conn->connection, XCB_PROP_MODE_REPLACE, win, _NET_WM_WINDOW_OPACITY, XCB_ATOM_CARDINAL, 32, 1, &values);
+ xcb_flush(conn->connection);
+}
+
+vector get_client_list(int screen) {
+ auto& conn = initialize();
+ xcb_ewmh_get_windows_reply_t reply;
+ if (xcb_ewmh_get_client_list_reply(conn, xcb_ewmh_get_client_list(conn, screen), &reply, nullptr)) {
+ return {reply.windows, reply.windows + reply.windows_len};
+ }
+ return {};
+}
+
+/**
+ * Retrieves the _NET_SUPPORTING_WM_CHECK atom on the given window
+ *
+ * @return The _NET_SUPPORTING_WM_CHECK window id or XCB_NONE in case of an error
+ */
+xcb_window_t get_supporting_wm_check(xcb_window_t win) {
+ auto& conn = initialize();
+ xcb_window_t result{};
+ if (xcb_ewmh_get_supporting_wm_check_reply(conn, xcb_ewmh_get_supporting_wm_check(conn, win), &result, nullptr)) {
+ return result;
+ }
+ return XCB_NONE;
+}
+
+/**
+ * Searches for the meta window spawned by the WM to indicate a compliant WM is active.
+ *
+ * The window has the following properties:
+ * * It's pointed to by the root window's _NET_SUPPORTING_WM_CHECK atom
+ * * Its own _NET_SUPPORTING_WM_CHECK atom is set and points to itself
+ *
+ * @return The meta window id or XCB_NONE if it wasn't found
+ */
+xcb_window_t get_ewmh_meta_window(xcb_window_t root) {
+ xcb_window_t wm_meta_window = ewmh_util::get_supporting_wm_check(root);
+
+ if (wm_meta_window != XCB_NONE && ewmh_util::get_supporting_wm_check(wm_meta_window) == wm_meta_window) {
+ return wm_meta_window;
}
- xcb_window_t get_active_window(int screen) {
- auto& conn = initialize();
- unsigned int win = XCB_NONE;
- xcb_ewmh_get_active_window_reply(conn, xcb_ewmh_get_active_window(conn, screen), &win, nullptr);
- return win;
- }
-
- void change_current_desktop(unsigned int desktop) {
- auto& conn = initialize();
- xcb_ewmh_request_change_current_desktop(conn, 0, desktop, XCB_CURRENT_TIME);
- xcb_flush(conn->connection);
- }
-
- unsigned int get_desktop_from_window(xcb_window_t window) {
- auto& conn = initialize();
- unsigned int desktop = XCB_NONE;
- xcb_ewmh_get_wm_desktop_reply(conn, xcb_ewmh_get_wm_desktop(conn, window), &desktop, nullptr);
- return desktop;
- }
-
- void set_wm_window_type(xcb_window_t win, vector types) {
- auto& conn = initialize();
- xcb_ewmh_set_wm_window_type(conn, win, types.size(), types.data());
- xcb_flush(conn->connection);
- }
-
- void set_wm_state(xcb_window_t win, vector states) {
- auto& conn = initialize();
- xcb_ewmh_set_wm_state(conn, win, states.size(), states.data());
- xcb_flush(conn->connection);
- }
-
- vector get_wm_state(xcb_window_t win) {
- auto& conn = initialize();
- xcb_ewmh_get_atoms_reply_t reply;
- if (xcb_ewmh_get_wm_state_reply(conn, xcb_ewmh_get_wm_state(conn, win), &reply, nullptr)) {
- return {reply.atoms, reply.atoms + reply.atoms_len};
- }
- return {};
- }
-
- void set_wm_pid(xcb_window_t win) {
- auto& conn = initialize();
- xcb_ewmh_set_wm_pid(conn, win, getpid());
- xcb_flush(conn->connection);
- }
-
- void set_wm_pid(xcb_window_t win, unsigned int pid) {
- auto& conn = initialize();
- xcb_ewmh_set_wm_pid(conn, win, pid);
- xcb_flush(conn->connection);
- }
-
- void set_wm_desktop(xcb_window_t win, unsigned int desktop) {
- auto& conn = initialize();
- xcb_ewmh_set_wm_desktop(conn, win, desktop);
- xcb_flush(conn->connection);
- }
-
- void set_wm_window_opacity(xcb_window_t win, unsigned long int values) {
- auto& conn = initialize();
- xcb_change_property(
- conn->connection, XCB_PROP_MODE_REPLACE, win, _NET_WM_WINDOW_OPACITY, XCB_ATOM_CARDINAL, 32, 1, &values);
- xcb_flush(conn->connection);
- }
-
- vector get_client_list(int screen) {
- auto& conn = initialize();
- xcb_ewmh_get_windows_reply_t reply;
- if (xcb_ewmh_get_client_list_reply(conn, xcb_ewmh_get_client_list(conn, screen), &reply, nullptr)) {
- return {reply.windows, reply.windows + reply.windows_len};
- }
- return {};
- }
+ return XCB_NONE;
+}
} // namespace ewmh_util
POLYBAR_NS_END