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