From eb38fb85ace95c6d6c5933d58bb95477ea02d0b5 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Tue, 11 Oct 2016 23:36:11 +0200 Subject: [PATCH] fix: Make the application work properly using i3wm --- README.md | 1 + include/components/bar.hpp | 108 ++++++++++++++++++++++++------ include/components/x11/atoms.hpp | 10 ++- include/components/x11/tray.hpp | 4 +- include/components/x11/window.hpp | 6 +- include/utils/i3.hpp | 60 +++++++++++++++++ src/main.cpp | 2 - 7 files changed, 165 insertions(+), 26 deletions(-) create mode 100644 include/utils/i3.hpp diff --git a/README.md b/README.md index 79fff928..50aaecfb 100644 --- a/README.md +++ b/README.md @@ -423,6 +423,7 @@ The configuration syntax is based on the `ini` file format. ; ; Currently supported WM's: ; bspwm + ; i3 ; Default: none wm-restack = bspwm ~~~ diff --git a/include/components/bar.hpp b/include/components/bar.hpp index d6d22df3..47a009c8 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -18,6 +18,7 @@ #include "components/x11/xlib.hpp" #include "components/x11/xutils.hpp" #include "utils/bspwm.hpp" +#include "utils/i3.hpp" #include "utils/math.hpp" #include "utils/string.hpp" #include "utils/threading.hpp" @@ -28,7 +29,7 @@ namespace bar_signals { delegate::Signal1 action_click; }; -class bar : public xpp::event::sink { +class bar : public xpp::event::sink { public: /** * Construct bar @@ -244,23 +245,76 @@ class bar : public xpp::event::sink { m_log.trace("bar: Set _NET_WM_WINDOW_TYPE"); { - xcb_atom_t win_types[2] = {_NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL}; + // const uint32_t win_types[2] = {_NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL}; + const uint32_t win_types[1] = {_NET_WM_WINDOW_TYPE_DOCK}; m_connection.change_property_checked( - XCB_PROP_MODE_REPLACE, m_window, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, 2, &win_types); + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, 1, win_types); } m_log.trace("bar: Set _NET_WM_STATE"); { - xcb_atom_t win_states[2] = {_NET_WM_STATE_STICKY, _NET_WM_STATE_SKIP_TASKBAR}; + if (m_bar.width == m_bar.monitor->w) { + const uint32_t win_states[3] = { + _NET_WM_STATE_STICKY, _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_MAXIMIZED_VERT}; + m_connection.change_property_checked( + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STATE, XCB_ATOM_ATOM, 32, 3, win_states); + } else { + const uint32_t win_states[2] = {_NET_WM_STATE_STICKY, _NET_WM_STATE_SKIP_TASKBAR}; + m_connection.change_property_checked( + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STATE, XCB_ATOM_ATOM, 32, 2, win_states); + } + } + + m_log.trace("bar: Set _NET_WM_STRUT"); + { + // clang-format off + uint32_t none{0}; + uint32_t value_list[4]{ + static_cast(m_bar.x), // left + none, // right + m_bar.bottom ? none : m_bar.height + m_bar.offset_y, // top + m_bar.bottom ? m_bar.height + m_bar.offset_y : none, // bottom + }; + // clang-format on m_connection.change_property_checked( - XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STATE, XCB_ATOM_ATOM, 32, 2, &win_states); + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, 4, value_list); + } + + m_log.trace("bar: Set _NET_WM_STRUT_PARTIAL"); + { + // clang-format off + uint32_t none{0}; + const uint32_t value_list[12]{ + static_cast(m_bar.x), // left + none, // right + m_bar.bottom ? none : m_bar.height + m_bar.offset_y, // top + m_bar.bottom ? m_bar.height + m_bar.offset_y : none, // bottom + none, // left_start_y + none, // left_end_y + none, // right_start_y + none, // right_end_y + m_bar.bottom ? none : m_bar.x, // top_start_x + m_bar.bottom ? none : static_cast(m_bar.x + m_bar.width), // top_end_x + m_bar.bottom ? m_bar.x : none, // bottom_start_x + m_bar.bottom ? static_cast(m_bar.x + m_bar.width) : none, // bottom_end_x + }; + // clang-format on + m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_window, _NET_WM_STRUT_PARTIAL, + XCB_ATOM_CARDINAL, 32, 12, value_list); + } + + m_log.trace("bar: Set _NET_WM_DESKTOP"); + { + const uint32_t value_list[1]{-1u}; + m_connection.change_property_checked( + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, value_list); } m_log.trace("bar: Set _NET_WM_PID"); { - int pid = getpid(); + const uint32_t value_list[1]{uint32_t(getpid())}; m_connection.change_property_checked( - XCB_PROP_MODE_REPLACE, m_window, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid); + XCB_PROP_MODE_REPLACE, m_window, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, value_list); } m_log.trace("bar: Create pixmap"); @@ -281,14 +335,27 @@ class bar : public xpp::event::sink { try { auto wm_restack = m_conf.get(bs, "wm-restack"); + auto restacked = false; if (wm_restack == "bspwm") { - if (bspwm_util::restack_above_root(m_connection, m_bar.monitor, m_window)) - m_log.info("Successfully restacked window above Bspwm root"); - else - m_log.err("Failed to restack bar window above Bspwm root"); + restacked = bspwm_util::restack_above_root(m_connection, m_bar.monitor, m_window); + + } else if (wm_restack == "i3" && m_bar.dock) { + restacked = i3_util::restack_above_root(m_connection, m_bar.monitor, m_window); + + } else if (wm_restack == "i3" && !m_bar.dock) { + m_log.warn("Ignoring restack of i3 window (not needed when dock = false)"); + wm_restack.clear(); + } else { - m_log.warn("Unsupported wm-restack option '%s'", wm_restack); + m_log.warn("Ignoring unsupported wm-restack option '%s'", wm_restack); + wm_restack.clear(); + } + + if (restacked) { + m_log.info("Successfully restacked bar window"); + } else if (!wm_restack.empty()) { + m_log.err("Failed to restack bar window"); } } catch (const key_error& err) { } @@ -296,13 +363,6 @@ class bar : public xpp::event::sink { // }}} // Create graphic contexts {{{ - // XCB_GC_LINE_WIDTH - // XCB_GC_LINE_STYLE - // -- XCB_LINE_STYLE_SOLID - // xcb_poly_line (connection, XCB_COORD_MODE_PREVIOUS, window, foreground, 4, polyline); - // xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, drawable, gc, 2, (xcb_point_t[]) { {10, 10}, {100, - // 10} }); - m_log.trace("bar: Create graphic contexts"); { // clang-format off @@ -598,6 +658,16 @@ class bar : public xpp::event::sink { } } // }}} + /** + * Event handler for XCB_EXPOSE events + */ + void handle(const evt::expose& evt) { // {{{ + if (evt->window != m_window) + return; + m_log.trace("bar: Received expose event"); + flush(); + } // }}} + protected: /** * Handle alignment update diff --git a/include/components/x11/atoms.hpp b/include/components/x11/atoms.hpp index 27842522..af066dea 100644 --- a/include/components/x11/atoms.hpp +++ b/include/components/x11/atoms.hpp @@ -10,6 +10,7 @@ struct cached_atom { }; static xcb_atom_t _NET_WM_NAME; +static xcb_atom_t _NET_WM_DESKTOP; static xcb_atom_t _NET_WM_WINDOW_TYPE; static xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK; static xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL; @@ -18,6 +19,9 @@ static xcb_atom_t _NET_WM_STATE; static xcb_atom_t _NET_WM_STATE_STICKY; static xcb_atom_t _NET_WM_STATE_SKIP_TASKBAR; static xcb_atom_t _NET_WM_STATE_ABOVE; +static xcb_atom_t _NET_WM_STATE_MAXIMIZED_VERT; +static xcb_atom_t _NET_WM_STRUT; +static xcb_atom_t _NET_WM_STRUT_PARTIAL; static xcb_atom_t WM_PROTOCOLS; static xcb_atom_t WM_DELETE_WINDOW; static xcb_atom_t _XEMBED; @@ -29,8 +33,9 @@ static xcb_atom_t _NET_SYSTEM_TRAY_ORIENTATION; static xcb_atom_t WM_TAKE_FOCUS; // clang-format off -static cached_atom ATOMS[18] = { +static cached_atom ATOMS[22] = { {"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &_NET_WM_NAME}, + {"_NET_WM_DESKTOP", sizeof("_NET_WM_DESKTOP") - 1, &_NET_WM_DESKTOP}, {"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1, &_NET_WM_WINDOW_TYPE}, {"_NET_WM_WINDOW_TYPE_DOCK", sizeof("_NET_WM_WINDOW_TYPE_DOCK") - 1, &_NET_WM_WINDOW_TYPE_DOCK}, {"_NET_WM_WINDOW_TYPE_NORMAL", sizeof("_NET_WM_WINDOW_TYPE_NORMAL") - 1, &_NET_WM_WINDOW_TYPE_NORMAL}, @@ -39,6 +44,9 @@ static cached_atom ATOMS[18] = { {"_NET_WM_STATE_STICKY", sizeof("_NET_WM_STATE_STICKY") - 1, &_NET_WM_STATE_STICKY}, {"_NET_WM_STATE_SKIP_TASKBAR", sizeof("_NET_WM_STATE_SKIP_TASKBAR") - 1, &_NET_WM_STATE_SKIP_TASKBAR}, {"_NET_WM_STATE_ABOVE", sizeof("_NET_WM_STATE_ABOVE") - 1, &_NET_WM_STATE_ABOVE}, + {"_NET_WM_STATE_MAXIMIZED_VERT", sizeof("_NET_WM_STATE_MAXIMIZED_VERT") - 1, &_NET_WM_STATE_MAXIMIZED_VERT}, + {"_NET_WM_STRUT", sizeof("_NET_WM_STRUT") - 1, &_NET_WM_STRUT}, + {"_NET_WM_STRUT_PARTIAL", sizeof("_NET_WM_STRUT_PARTIAL") - 1, &_NET_WM_STRUT_PARTIAL}, {"WM_PROTOCOLS", sizeof("WM_PROTOCOLS") - 1, &WM_PROTOCOLS}, {"WM_DELETE_WINDOW", sizeof("WM_DELETE_WINDOW") - 1, &WM_DELETE_WINDOW}, {"_XEMBED", sizeof("_XEMBED") - 1, &_XEMBED}, diff --git a/include/components/x11/tray.hpp b/include/components/x11/tray.hpp index 2e19c95e..af8ba025 100644 --- a/include/components/x11/tray.hpp +++ b/include/components/x11/tray.hpp @@ -275,8 +275,8 @@ class traymanager m_logger.trace("tray: Create tray window %s, (%ix%i+%i+%i)", m_connection.id(m_tray), m_settings.width, m_settings.height, x, y); auto scr = m_connection.screen(); - const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; - const uint32_t values[2]{m_settings.background, + const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; + const uint32_t values[3]{m_settings.background, true, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY}; m_connection.create_window_checked(scr->root_depth, m_tray, scr->root, x, y, m_settings.width + m_settings.spacing * 2, m_settings.height + m_settings.spacing * 2, 0, diff --git a/include/components/x11/window.hpp b/include/components/x11/window.hpp index 99ac7ff8..d2f35c90 100644 --- a/include/components/x11/window.hpp +++ b/include/components/x11/window.hpp @@ -137,10 +137,12 @@ class window : public xpp::window { // // window operator<<(cw_flush f) { // if (f.checked) -// m_connection.create_window_checked(m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, +// m_connection.create_window_checked(m_depth, m_window, m_parent, m_x, m_y, m_width, +// m_height, // m_border, m_class, m_visual, m_mask, m_params); // else -// m_connection.create_window(m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, m_border, +// m_connection.create_window(m_depth, m_window, m_parent, m_x, m_y, m_width, m_height, +// m_border, // m_class, m_visual, m_mask, m_params); // return m_window; // } diff --git a/include/utils/i3.hpp b/include/utils/i3.hpp new file mode 100644 index 00000000..714562dd --- /dev/null +++ b/include/utils/i3.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include "common.hpp" +#include "components/x11/connection.hpp" +#include "components/x11/randr.hpp" +#include "config.hpp" +#include "utils/socket.hpp" +#include "utils/string.hpp" + +LEMONBUDDY_NS + +namespace i3_util { + /** + * Get all i3 root windows + */ + auto root_windows(connection& conn, string output_name = "") { + vector roots; + auto children = conn.query_tree(conn.screen()->root).children(); + + for (auto it = children.begin(); it != children.end(); it++) { + auto cookie = xcb_icccm_get_wm_name(conn, *it); + xcb_icccm_get_text_property_reply_t reply; + + if (xcb_icccm_get_wm_name_reply(conn, cookie, &reply, nullptr) == 0) + continue; + + if (("[i3 con] output " + output_name).compare(0, 16 + output_name.length(), reply.name) != 0) + continue; + + roots.emplace_back(*it); + } + + return roots; + } + + /** + * Restack given window above the i3 root window + * defined for the given monitor + * + * Fixes the issue with always-on-top window's + */ + bool restack_above_root(connection& conn, const monitor_t& mon, const xcb_window_t win) { + for (auto&& root : root_windows(conn, mon->name)) { + const uint32_t value_mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE; + const uint32_t value_list[2]{root, XCB_STACK_MODE_ABOVE}; + + conn.configure_window_checked(win, value_mask, value_list); + conn.flush(); + + return true; + } + + return false; + } +} + +LEMONBUDDY_NS_END diff --git a/src/main.cpp b/src/main.cpp index 8a58231a..10d6b24d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,8 +8,6 @@ #include "components/logger.hpp" #include "components/x11/xutils.hpp" #include "config.hpp" -#include "utils/bspwm.hpp" -#include "utils/file.hpp" #include "utils/inotify.hpp" using namespace lemonbuddy;