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:
Michael Carlberg 2016-10-15 20:10:40 +02:00
parent 0128168c51
commit 17e16d18a9
2 changed files with 81 additions and 2 deletions

View File

@ -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, &params, border_pixel, m_bar.background.value());
XCB_AUX_ADD_PARAM(&mask, &params, colormap, m_colormap);
XCB_AUX_ADD_PARAM(&mask, &params, override_redirect, m_bar.dock);
XCB_AUX_ADD_PARAM(&mask, &params, event_mask, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS);
XCB_AUX_ADD_PARAM(&mask, &params, 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, &params);
}
@ -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

View File

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