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