From 17e16d18a987283f24acef2fe86a18d0da6c60d5 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Sat, 15 Oct 2016 20:10:40 +0200 Subject: [PATCH] fix(i3): Make tray copy the bar' visibility state This adds a fallback routine where the tray window will get notified whenever the bar window changes its visibility state. Required in case of failure to restack the tray container above the bar window in the window stack. Fixes jaagr/lemonbuddy#95 --- include/components/bar.hpp | 39 +++++++++++++++++++++++++++-- include/components/x11/tray.hpp | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 4e6a4a13..7ce477e6 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -28,7 +28,7 @@ LEMONBUDDY_NS -class bar : public xpp::event::sink { +class bar : public xpp::event::sink { public: /** * Construct bar @@ -230,7 +230,7 @@ class bar : public xpp::event::sink { XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, m_bar.background.value()); XCB_AUX_ADD_PARAM(&mask, ¶ms, colormap, m_colormap); XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, m_bar.dock); - XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS); + XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS); // clang-format on m_window.create_checked(m_bar.x, m_bar.y, m_bar.width, m_bar.height, mask, ¶ms); } @@ -644,6 +644,41 @@ class bar : public xpp::event::sink { flush(); } // }}} + /** + * Event handler for XCB_PROPERTY_NOTIFY events + * + * Used to emit events whenever the bar window's + * visibility gets changes. This allows us to toggle the + * state of the tray container even though the tray + * window restacking failed. + * + * This is used as a fallback for tedious WM's, like i3. + * + * Some might call it a dirty hack, others a crappy + * solution... I choose to call it a masterpiece! Plus + * it's not really any overhead worth talking about. + */ + void handle(const evt::property_notify& evt) { // {{{ + if (evt->window == m_window && evt->atom == WM_STATE) { + if (g_signals::bar::visibility_change.empty()) { + return; + } + try { + auto attr = m_connection.get_window_attributes(m_window); + if (attr->map_state == XCB_MAP_STATE_VIEWABLE) + g_signals::bar::visibility_change.emit(true); + else if (attr->map_state == XCB_MAP_STATE_UNVIEWABLE) + g_signals::bar::visibility_change.emit(false); + else if (attr->map_state == XCB_MAP_STATE_UNMAPPED) + g_signals::bar::visibility_change.emit(false); + else + g_signals::bar::visibility_change.emit(true); + } catch (const std::exception& err) { + m_log.warn("Failed to emit bar window's visibility change event"); + } + } + } // }}} + protected: /** * Handle alignment update diff --git a/include/components/x11/tray.hpp b/include/components/x11/tray.hpp index d9705e5c..67e7deb8 100644 --- a/include/components/x11/tray.hpp +++ b/include/components/x11/tray.hpp @@ -145,6 +145,13 @@ class traymanager m_sinkattached = true; } + // Listen for visibility change events on the bar window + if (!m_restacked) { + g_signals::bar::visibility_change.connect(this, &traymanager::bar_visibility_change); + } + + // Attempt to get control of the systray selection then + // notify clients waiting for a manager. acquire_selection(); notify_clients(); @@ -185,6 +192,10 @@ class traymanager m_sinkattached = false; } + if (!m_restacked) { + g_signals::bar::visibility_change.disconnect(this, &traymanager::bar_visibility_change); + } + // Dismiss all clients by reparenting them to the root window m_logger.trace("tray: Unembed clients"); for (auto&& client : m_clients) { @@ -211,6 +222,12 @@ class traymanager * reposition embedded clients */ void reconfigure() { + // Ignore reconfigure requests when the + // tray window is in the pseudo-hidden state + if (m_hidden) { + return; + } + uint32_t width = 0; uint16_t mapped_clients = 0; @@ -266,6 +283,29 @@ class traymanager } protected: + /** + * Signal handler connected to the bar window's visibility change signal. + * This is used as a fallback in case the window restacking fails. It will + * toggle the tray window whenever the visibility of the bar window changes. + */ + void bar_visibility_change(bool state) { + try { + // Ignore unchanged states + if (m_hidden == !state) + return; + + // Update the psuedo-state + m_hidden = !state; + + if (state && !m_mapped) + m_connection.map_window_checked(m_tray); + else if (!state && m_mapped) + m_connection.unmap_window_checked(m_tray); + } catch (const std::exception& err) { + m_logger.warn("Failed to un-/map the tray window (%s)", err.what()); + } + } + /** * Calculate the tray window's horizontal position */ @@ -328,6 +368,7 @@ class traymanager const uint32_t value_list[2]{m_settings.sibling, XCB_STACK_MODE_ABOVE}; m_connection.configure_window_checked(m_tray, value_mask, value_list); m_connection.flush(); + m_restacked = true; } } catch (const std::exception& err) { auto id = m_connection.id(m_settings.sibling); @@ -725,9 +766,12 @@ class traymanager stateflag m_activated{false}; stateflag m_mapped{false}; + stateflag m_hidden{false}; stateflag m_sinkattached{false}; thread m_notifythread; + + bool m_restacked = false; }; // }}}