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
This commit is contained in:
parent
0128168c51
commit
17e16d18a9
2 changed files with 81 additions and 2 deletions
|
@ -28,7 +28,7 @@
|
|||
|
||||
LEMONBUDDY_NS
|
||||
|
||||
class bar : public xpp::event::sink<evt::button_press, evt::expose> {
|
||||
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify> {
|
||||
public:
|
||||
/**
|
||||
* Construct bar
|
||||
|
@ -230,7 +230,7 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose> {
|
|||
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<evt::button_press, evt::expose> {
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
// }}}
|
||||
|
|
Loading…
Reference in a new issue