refactor(tray): Better support for conflicting managers
Listen for destroy event on the new manager window when systray selection is lost. If the window gets destroyed we can re-activate the manager.
This commit is contained in:
parent
2529ec0520
commit
91a9bb6d95
@ -137,11 +137,11 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
|||||||
message(STATUS " + relwithdebinfo flags:: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
message(STATUS " + relwithdebinfo flags:: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||||
endif()
|
endif()
|
||||||
if(CXXLIB_CLANG)
|
if(CXXLIB_CLANG)
|
||||||
message(STATUS "Linking C++ library: libc++")
|
message(STATUS " Linking C++ library: libc++")
|
||||||
elseif(CXXLIB_GCC)
|
elseif(CXXLIB_GCC)
|
||||||
message(STATUS "Linking C++ library: libstdc++")
|
message(STATUS " Linking C++ library: libstdc++")
|
||||||
else()
|
else()
|
||||||
message(STATUS "Linking C++ library: system default")
|
message(STATUS " Linking C++ library: system default")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "---------------------------")
|
message(STATUS "---------------------------")
|
||||||
message(STATUS " Build testsuite ${BUILD_TESTS}")
|
message(STATUS " Build testsuite ${BUILD_TESTS}")
|
||||||
|
@ -84,6 +84,7 @@ class controller {
|
|||||||
if (m_traymanager) {
|
if (m_traymanager) {
|
||||||
m_log.trace("controller: Deactivate tray manager");
|
m_log.trace("controller: Deactivate tray manager");
|
||||||
m_traymanager->deactivate();
|
m_traymanager->deactivate();
|
||||||
|
m_traymanager.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_log.trace("controller: Deconstruct bar instance");
|
m_log.trace("controller: Deconstruct bar instance");
|
||||||
@ -243,8 +244,7 @@ class controller {
|
|||||||
m_log.trace("controller: Listen for X events");
|
m_log.trace("controller: Listen for X events");
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
auto evt = m_connection.wait_for_event();
|
auto evt = m_connection.wait_for_event();
|
||||||
|
if (evt != nullptr && m_running)
|
||||||
if (evt != nullptr)
|
|
||||||
m_connection.dispatch_event(evt);
|
m_connection.dispatch_event(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -177,7 +177,8 @@ class connection : public xpp_connection {
|
|||||||
* Dispatch event through the registry
|
* Dispatch event through the registry
|
||||||
*/
|
*/
|
||||||
void dispatch_event(const shared_ptr<xcb_generic_event_t>& evt) {
|
void dispatch_event(const shared_ptr<xcb_generic_event_t>& evt) {
|
||||||
m_registry.dispatch(forward<decltype(evt)>(evt));
|
if (evt != nullptr)
|
||||||
|
m_registry.dispatch(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -34,6 +34,10 @@ class trayclient {
|
|||||||
m_xembed->flags = XEMBED_MAPPED;
|
m_xembed->flags = XEMBED_MAPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~trayclient() {
|
||||||
|
xembed::unembed(m_connection, window(), m_connection.root());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Match given window against client window
|
* Match given window against client window
|
||||||
*/
|
*/
|
||||||
@ -105,8 +109,7 @@ class traymanager
|
|||||||
evt::configure_request, evt::resize_request, evt::selection_clear, evt::property_notify,
|
evt::configure_request, evt::resize_request, evt::selection_clear, evt::property_notify,
|
||||||
evt::reparent_notify, evt::destroy_notify, evt::map_notify, evt::unmap_notify> {
|
evt::reparent_notify, evt::destroy_notify, evt::map_notify, evt::unmap_notify> {
|
||||||
public:
|
public:
|
||||||
explicit traymanager(connection& conn, const logger& logger)
|
explicit traymanager(connection& conn, const logger& logger) : m_connection(conn), m_log(logger) {
|
||||||
: m_connection(conn), m_logger(logger) {
|
|
||||||
m_connection.attach_sink(this, 2);
|
m_connection.attach_sink(this, 2);
|
||||||
m_sinkattached = true;
|
m_sinkattached = true;
|
||||||
}
|
}
|
||||||
@ -124,8 +127,6 @@ class traymanager
|
|||||||
auto bootstrap(tray_settings settings) {
|
auto bootstrap(tray_settings settings) {
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
query_atom();
|
query_atom();
|
||||||
create_window();
|
|
||||||
set_wmhints();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,11 +134,22 @@ class traymanager
|
|||||||
*/
|
*/
|
||||||
void activate() {
|
void activate() {
|
||||||
if (m_activated) {
|
if (m_activated) {
|
||||||
// m_logger.warn("Tray is already activated...");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_logger.info("Activating traymanager");
|
if (m_tray == XCB_NONE) {
|
||||||
|
try {
|
||||||
|
create_window();
|
||||||
|
set_wmhints();
|
||||||
|
} catch (const std::exception& err) {
|
||||||
|
m_log.err(err.what());
|
||||||
|
m_log.err("Cannot activate traymanager... failed to setup window");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.info("Activating traymanager");
|
||||||
|
m_activated = true;
|
||||||
|
|
||||||
if (!m_sinkattached) {
|
if (!m_sinkattached) {
|
||||||
m_connection.attach_sink(this, 2);
|
m_connection.attach_sink(this, 2);
|
||||||
@ -145,7 +157,7 @@ class traymanager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Listen for visibility change events on the bar window
|
// Listen for visibility change events on the bar window
|
||||||
if (!m_restacked) {
|
if (!m_restacked && !g_signals::bar::visibility_change) {
|
||||||
g_signals::bar::visibility_change =
|
g_signals::bar::visibility_change =
|
||||||
bind(&traymanager::bar_visibility_change, this, std::placeholders::_1);
|
bind(&traymanager::bar_visibility_change, this, std::placeholders::_1);
|
||||||
}
|
}
|
||||||
@ -153,27 +165,15 @@ class traymanager
|
|||||||
// Attempt to get control of the systray selection then
|
// Attempt to get control of the systray selection then
|
||||||
// notify clients waiting for a manager.
|
// notify clients waiting for a manager.
|
||||||
acquire_selection();
|
acquire_selection();
|
||||||
notify_clients();
|
|
||||||
|
|
||||||
m_activated = true;
|
// If replacing an existing manager or if re-activating from getting
|
||||||
m_connection.flush();
|
// replaced, we delay the notification broadcast to allow the clients
|
||||||
|
// to get unembedded...
|
||||||
// Send delayed notifications
|
if (m_othermanager != XCB_NONE)
|
||||||
// This is done to ensure that any stray tray clients
|
|
||||||
// gets notified in case the MANAGER is deactivated/activated
|
|
||||||
// in a short period of time
|
|
||||||
m_notifythread = thread([this] {
|
|
||||||
for (auto i = 0; m_activated && i < 3; i++) {
|
|
||||||
this_thread::sleep_for(1s);
|
this_thread::sleep_for(1s);
|
||||||
try {
|
|
||||||
if (!m_activated)
|
|
||||||
break;
|
|
||||||
notify_clients();
|
notify_clients();
|
||||||
|
|
||||||
m_connection.flush();
|
m_connection.flush();
|
||||||
} catch (...) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,40 +181,46 @@ class traymanager
|
|||||||
*/
|
*/
|
||||||
void deactivate() {
|
void deactivate() {
|
||||||
if (!m_activated) {
|
if (!m_activated) {
|
||||||
// m_logger.warn("Tray is already deactivated...");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_logger.info("Deactivating traymanager");
|
m_log.info("Deactivating traymanager");
|
||||||
|
|
||||||
if (m_sinkattached) {
|
|
||||||
m_connection.detach_sink(this, 2);
|
|
||||||
m_sinkattached = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_restacked) {
|
|
||||||
g_signals::bar::visibility_change =
|
|
||||||
bind(&traymanager::bar_visibility_change, this, std::placeholders::_1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss all clients by reparenting them to the root window
|
|
||||||
m_logger.trace("tray: Unembed clients");
|
|
||||||
for (auto&& client : m_clients) {
|
|
||||||
xembed::unembed(m_connection, client->window(), m_connection.root());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_activated = false;
|
m_activated = false;
|
||||||
|
|
||||||
if (m_notifythread.joinable())
|
if (m_delayed_activation.joinable())
|
||||||
m_notifythread.join();
|
m_delayed_activation.join();
|
||||||
|
|
||||||
try {
|
if (g_signals::tray::report_slotcount) {
|
||||||
if (m_connection.get_selection_owner(m_atom).owner<xcb_window_t>() == m_tray)
|
m_log.trace("tray: Report empty slotcount");
|
||||||
m_connection.set_selection_owner_checked(XCB_NONE, m_atom, XCB_CURRENT_TIME);
|
g_signals::tray::report_slotcount(0);
|
||||||
} catch (...) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_signals::bar::visibility_change) {
|
||||||
|
m_log.trace("tray: Clear callback handlers");
|
||||||
|
g_signals::bar::visibility_change = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.trace("tray: Unembed all clients");
|
||||||
|
m_clients.clear();
|
||||||
|
|
||||||
|
if (m_connection.get_selection_owner_unchecked(m_atom).owner<xcb_window_t>() == m_tray) {
|
||||||
|
m_log.trace("tray: Unset selection owner");
|
||||||
|
m_connection.set_selection_owner(XCB_NONE, m_atom, XCB_CURRENT_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_tray != XCB_NONE) {
|
||||||
|
if (m_mapped) {
|
||||||
|
m_log.trace("tray: Unmap window");
|
||||||
m_connection.unmap_window(m_tray);
|
m_connection.unmap_window(m_tray);
|
||||||
|
m_mapped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.trace("tray: Destroy window");
|
||||||
|
m_connection.destroy_window(m_tray);
|
||||||
|
m_tray = XCB_NONE;
|
||||||
|
m_hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
m_connection.flush();
|
m_connection.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +301,7 @@ class traymanager
|
|||||||
else if (!state && m_mapped)
|
else if (!state && m_mapped)
|
||||||
m_connection.unmap_window_checked(m_tray);
|
m_connection.unmap_window_checked(m_tray);
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
m_logger.warn("Failed to un-/map the tray window (%s)", err.what());
|
m_log.warn("Failed to un-/map the tray window (%s)", err.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +337,7 @@ class traymanager
|
|||||||
* Find the systray selection atom
|
* Find the systray selection atom
|
||||||
*/
|
*/
|
||||||
void query_atom() {
|
void query_atom() {
|
||||||
m_logger.trace("tray: Find systray selection atom for the default screen");
|
m_log.trace("tray: Find systray selection atom for the default screen");
|
||||||
string name{"_NET_SYSTEM_TRAY_S" + to_string(m_connection.default_screen())};
|
string name{"_NET_SYSTEM_TRAY_S" + to_string(m_connection.default_screen())};
|
||||||
auto reply = m_connection.intern_atom(false, name.length(), name.c_str());
|
auto reply = m_connection.intern_atom(false, name.length(), name.c_str());
|
||||||
m_atom = reply.atom();
|
m_atom = reply.atom();
|
||||||
@ -344,7 +350,7 @@ class traymanager
|
|||||||
auto x = calculate_x();
|
auto x = calculate_x();
|
||||||
auto y = calculate_y();
|
auto y = calculate_y();
|
||||||
m_tray = m_connection.generate_id();
|
m_tray = m_connection.generate_id();
|
||||||
m_logger.trace("tray: Create tray window %s, (%ix%i+%i+%i)", m_connection.id(m_tray),
|
m_log.trace("tray: Create tray window %s, (%ix%i+%i+%i)", m_connection.id(m_tray),
|
||||||
m_settings.width, m_settings.height, x, y);
|
m_settings.width, m_settings.height, x, y);
|
||||||
auto scr = m_connection.screen();
|
auto scr = m_connection.screen();
|
||||||
const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
|
const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
|
||||||
@ -365,7 +371,7 @@ class traymanager
|
|||||||
}
|
}
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
auto id = m_connection.id(m_settings.sibling);
|
auto id = m_connection.id(m_settings.sibling);
|
||||||
m_logger.trace("tray: Failed to put tray above %s in the stack (%s)", id, err.what());
|
m_log.trace("tray: Failed to put tray above %s in the stack (%s)", id, err.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,36 +379,36 @@ class traymanager
|
|||||||
* Set window WM hints
|
* Set window WM hints
|
||||||
*/
|
*/
|
||||||
void set_wmhints() {
|
void set_wmhints() {
|
||||||
m_logger.trace("tray: Set window WM_NAME / WM_CLASS", m_connection.id(m_tray));
|
m_log.trace("tray: Set window WM_NAME / WM_CLASS", m_connection.id(m_tray));
|
||||||
xcb_icccm_set_wm_name(m_connection, m_tray, XCB_ATOM_STRING, 8, 22, TRAY_WM_NAME);
|
xcb_icccm_set_wm_name(m_connection, m_tray, XCB_ATOM_STRING, 8, 22, TRAY_WM_NAME);
|
||||||
xcb_icccm_set_wm_class(m_connection, m_tray, 15, TRAY_WM_CLASS);
|
xcb_icccm_set_wm_class(m_connection, m_tray, 15, TRAY_WM_CLASS);
|
||||||
|
|
||||||
m_logger.trace("tray: Set window WM_PROTOCOLS");
|
m_log.trace("tray: Set window WM_PROTOCOLS");
|
||||||
vector<xcb_atom_t> wm_flags;
|
vector<xcb_atom_t> wm_flags;
|
||||||
wm_flags.emplace_back(WM_DELETE_WINDOW);
|
wm_flags.emplace_back(WM_DELETE_WINDOW);
|
||||||
wm_flags.emplace_back(WM_TAKE_FOCUS);
|
wm_flags.emplace_back(WM_TAKE_FOCUS);
|
||||||
xcb_icccm_set_wm_protocols(
|
xcb_icccm_set_wm_protocols(
|
||||||
m_connection, m_tray, WM_PROTOCOLS, wm_flags.size(), wm_flags.data());
|
m_connection, m_tray, WM_PROTOCOLS, wm_flags.size(), wm_flags.data());
|
||||||
|
|
||||||
m_logger.trace("tray: Set window _NET_WM_WINDOW_TYPE");
|
m_log.trace("tray: Set window _NET_WM_WINDOW_TYPE");
|
||||||
vector<xcb_atom_t> types;
|
vector<xcb_atom_t> types;
|
||||||
types.emplace_back(_NET_WM_WINDOW_TYPE_DOCK);
|
types.emplace_back(_NET_WM_WINDOW_TYPE_DOCK);
|
||||||
types.emplace_back(_NET_WM_WINDOW_TYPE_NORMAL);
|
types.emplace_back(_NET_WM_WINDOW_TYPE_NORMAL);
|
||||||
m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM,
|
m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM,
|
||||||
32, types.size(), types.data());
|
32, types.size(), types.data());
|
||||||
|
|
||||||
m_logger.trace("tray: Set window _NET_WM_STATE");
|
m_log.trace("tray: Set window _NET_WM_STATE");
|
||||||
vector<xcb_atom_t> states;
|
vector<xcb_atom_t> states;
|
||||||
states.emplace_back(_NET_WM_STATE_SKIP_TASKBAR);
|
states.emplace_back(_NET_WM_STATE_SKIP_TASKBAR);
|
||||||
m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_STATE, XCB_ATOM_ATOM, 32,
|
m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_STATE, XCB_ATOM_ATOM, 32,
|
||||||
states.size(), states.data());
|
states.size(), states.data());
|
||||||
|
|
||||||
m_logger.trace("tray: Set window _NET_SYSTEM_TRAY_ORIENTATION");
|
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_ORIENTATION");
|
||||||
const uint32_t values[1]{_NET_SYSTEM_TRAY_ORIENTATION_HORZ};
|
const uint32_t values[1]{_NET_SYSTEM_TRAY_ORIENTATION_HORZ};
|
||||||
m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_tray,
|
m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_tray,
|
||||||
_NET_SYSTEM_TRAY_ORIENTATION, _NET_SYSTEM_TRAY_ORIENTATION, 32, 1, values);
|
_NET_SYSTEM_TRAY_ORIENTATION, _NET_SYSTEM_TRAY_ORIENTATION, 32, 1, values);
|
||||||
|
|
||||||
m_logger.trace("tray: Set window _NET_WM_PID");
|
m_log.trace("tray: Set window _NET_WM_PID");
|
||||||
int pid = getpid();
|
int pid = getpid();
|
||||||
m_connection.change_property(
|
m_connection.change_property(
|
||||||
XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
|
XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
|
||||||
@ -415,27 +421,24 @@ class traymanager
|
|||||||
xcb_window_t owner = m_connection.get_selection_owner_unchecked(m_atom)->owner;
|
xcb_window_t owner = m_connection.get_selection_owner_unchecked(m_atom)->owner;
|
||||||
|
|
||||||
if (owner == m_tray) {
|
if (owner == m_tray) {
|
||||||
m_logger.info("tray: Already managing the systray selection");
|
m_log.info("tray: Already managing the systray selection");
|
||||||
return;
|
return;
|
||||||
} else if (owner != XCB_NONE) {
|
} else if ((m_othermanager = owner) != XCB_NONE) {
|
||||||
m_logger.info("tray: Replace existing selection manager %s", m_connection.id(owner));
|
m_log.info("Replacing selection manager %s", m_connection.id(owner));
|
||||||
// track_selection_owner(owner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_logger.trace("tray: Change selection owner to %s", m_connection.id(m_tray));
|
m_log.trace("tray: Change selection owner to %s", m_connection.id(m_tray));
|
||||||
m_connection.set_selection_owner_checked(m_tray, m_atom, XCB_CURRENT_TIME);
|
m_connection.set_selection_owner_checked(m_tray, m_atom, XCB_CURRENT_TIME);
|
||||||
|
|
||||||
if (m_connection.get_selection_owner_unchecked(m_atom)->owner != m_tray)
|
if (m_connection.get_selection_owner_unchecked(m_atom)->owner != m_tray)
|
||||||
throw application_error("Failed to get control of the systray selection");
|
throw application_error("Failed to get control of the systray selection");
|
||||||
|
|
||||||
m_othermanager = XCB_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify pending clients about the new systray MANAGER
|
* Notify pending clients about the new systray MANAGER
|
||||||
*/
|
*/
|
||||||
void notify_clients() {
|
void notify_clients() {
|
||||||
m_logger.trace("tray: Broadcast new selection manager to pending clients");
|
m_log.trace("tray: Broadcast new selection manager to pending clients");
|
||||||
auto message = m_connection.make_client_message(MANAGER, m_connection.root());
|
auto message = m_connection.make_client_message(MANAGER, m_connection.root());
|
||||||
message->data.data32[0] = XCB_CURRENT_TIME;
|
message->data.data32[0] = XCB_CURRENT_TIME;
|
||||||
message->data.data32[1] = m_atom;
|
message->data.data32[1] = m_atom;
|
||||||
@ -447,21 +450,17 @@ class traymanager
|
|||||||
* Track changes to the given selection owner
|
* Track changes to the given selection owner
|
||||||
* If it gets destroyed or goes away we can reactivate the traymanager
|
* If it gets destroyed or goes away we can reactivate the traymanager
|
||||||
*/
|
*/
|
||||||
// void track_selection_owner(xcb_window_t owner = XCB_NONE) {
|
void track_selection_owner(xcb_window_t owner) {
|
||||||
// try {
|
try {
|
||||||
// if (owner != XCB_NONE) {
|
if (owner == XCB_NONE)
|
||||||
// m_othermanager = owner;
|
return;
|
||||||
// } else {
|
m_log.trace("tray: Listen for events on the new selection window");
|
||||||
// m_othermanager = m_connection.get_selection_owner_unchecked(m_atom)->owner;
|
const uint32_t event_mask[1]{XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
||||||
// }
|
m_connection.change_window_attributes_checked(owner, XCB_CW_EVENT_MASK, event_mask);
|
||||||
// m_logger.trace("tray: Listen for events on the new selection window");
|
} catch (const xpp::x::error::window& err) {
|
||||||
// const uint32_t event_mask[1]{XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
m_log.err("Failed to track selection owner");
|
||||||
// m_connection.change_window_attributes_checked(m_othermanager, XCB_CW_EVENT_MASK,
|
}
|
||||||
// event_mask);
|
}
|
||||||
// } catch (const xpp::x::error::window& err) {
|
|
||||||
// m_logger.err("Failed to track selection owner");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates a client's x position
|
* Calculates a client's x position
|
||||||
@ -480,13 +479,13 @@ class traymanager
|
|||||||
auto client = find_client(win);
|
auto client = find_client(win);
|
||||||
if (client) {
|
if (client) {
|
||||||
if (client->mapped()) {
|
if (client->mapped()) {
|
||||||
m_logger.trace("tray: Client %s is already embedded, skipping...", m_connection.id(win));
|
m_log.trace("tray: Client %s is already embedded, skipping...", m_connection.id(win));
|
||||||
} else {
|
} else {
|
||||||
m_logger.trace("tray: Refresh _XEMBED_INFO");
|
m_log.trace("tray: Refresh _XEMBED_INFO");
|
||||||
xembed::query(m_connection, win, client->xembed());
|
xembed::query(m_connection, win, client->xembed());
|
||||||
|
|
||||||
if ((client->xembed()->flags & XEMBED_MAPPED) == XEMBED_MAPPED) {
|
if ((client->xembed()->flags & XEMBED_MAPPED) == XEMBED_MAPPED) {
|
||||||
m_logger.trace("tray: XEMBED_MAPPED flag set, map client window...");
|
m_log.trace("tray: XEMBED_MAPPED flag set, map client window...");
|
||||||
m_connection.map_window(client->window());
|
m_connection.map_window(client->window());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -494,36 +493,36 @@ class traymanager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_logger.trace("tray: Process docking request from %s", m_connection.id(win));
|
m_log.trace("tray: Process docking request from %s", m_connection.id(win));
|
||||||
m_clients.emplace_back(make_shared<trayclient>(m_connection, win));
|
m_clients.emplace_back(make_shared<trayclient>(m_connection, win));
|
||||||
client = m_clients.back();
|
client = m_clients.back();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m_logger.trace("tray: Get client _XEMBED_INFO");
|
m_log.trace("tray: Get client _XEMBED_INFO");
|
||||||
xembed::query(m_connection, win, client->xembed());
|
xembed::query(m_connection, win, client->xembed());
|
||||||
} catch (const application_error& err) {
|
} catch (const application_error& err) {
|
||||||
m_logger.err(err.what());
|
m_log.err(err.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_logger.trace("tray: Add tray client window to the save set");
|
m_log.trace("tray: Add tray client window to the save set");
|
||||||
m_connection.change_save_set(XCB_SET_MODE_INSERT, client->window());
|
m_connection.change_save_set(XCB_SET_MODE_INSERT, client->window());
|
||||||
|
|
||||||
m_logger.trace("tray: Update tray client event mask");
|
m_log.trace("tray: Update tray client event mask");
|
||||||
const uint32_t event_mask[]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
const uint32_t event_mask[]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
||||||
m_connection.change_window_attributes(client->window(), XCB_CW_EVENT_MASK, event_mask);
|
m_connection.change_window_attributes(client->window(), XCB_CW_EVENT_MASK, event_mask);
|
||||||
|
|
||||||
m_logger.trace("tray: Reparent tray client");
|
m_log.trace("tray: Reparent tray client");
|
||||||
m_connection.reparent_window(client->window(), m_tray, m_settings.spacing, m_settings.spacing);
|
m_connection.reparent_window(client->window(), m_tray, m_settings.spacing, m_settings.spacing);
|
||||||
|
|
||||||
m_logger.trace("tray: Configure tray client size");
|
m_log.trace("tray: Configure tray client size");
|
||||||
const uint32_t values[]{m_settings.width, m_settings.height};
|
const uint32_t values[]{m_settings.width, m_settings.height};
|
||||||
m_connection.configure_window(
|
m_connection.configure_window(
|
||||||
client->window(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
|
client->window(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
|
||||||
|
|
||||||
m_logger.trace("tray: Send embbeded notification to tray client");
|
m_log.trace("tray: Send embbeded notification to tray client");
|
||||||
xembed::notify_embedded(m_connection, client->window(), m_tray, client->xembed()->version);
|
xembed::notify_embedded(m_connection, client->window(), m_tray, client->xembed()->version);
|
||||||
|
|
||||||
m_logger.trace("tray: Map tray client");
|
m_log.trace("tray: Map tray client");
|
||||||
m_connection.map_window(client->window());
|
m_connection.map_window(client->window());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,9 +530,11 @@ class traymanager
|
|||||||
* Event callback : XCB_EXPOSE
|
* Event callback : XCB_EXPOSE
|
||||||
*/
|
*/
|
||||||
void handle(const evt::expose& evt) {
|
void handle(const evt::expose& evt) {
|
||||||
if (!m_activated || m_clients.empty())
|
if (!m_activated)
|
||||||
return;
|
return;
|
||||||
m_logger.trace("tray: Received expose event for %s", m_connection.id(evt->window));
|
if (m_clients.empty())
|
||||||
|
return;
|
||||||
|
m_log.trace("tray: Received expose event for %s", m_connection.id(evt->window));
|
||||||
reconfigure();
|
reconfigure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +544,9 @@ class traymanager
|
|||||||
void handle(const evt::visibility_notify& evt) {
|
void handle(const evt::visibility_notify& evt) {
|
||||||
if (!m_activated || m_clients.empty())
|
if (!m_activated || m_clients.empty())
|
||||||
return;
|
return;
|
||||||
m_logger.trace("tray: Received visibility_notify for %s", m_connection.id(evt->window));
|
if (m_clients.empty())
|
||||||
|
return;
|
||||||
|
m_log.trace("tray: Received visibility_notify for %s", m_connection.id(evt->window));
|
||||||
reconfigure();
|
reconfigure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,8 +554,11 @@ class traymanager
|
|||||||
* Event callback : XCB_CLIENT_MESSAGE
|
* Event callback : XCB_CLIENT_MESSAGE
|
||||||
*/
|
*/
|
||||||
void handle(const evt::client_message& evt) {
|
void handle(const evt::client_message& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
|
|
||||||
if (evt->type == _NET_SYSTEM_TRAY_OPCODE && evt->format == 32) {
|
if (evt->type == _NET_SYSTEM_TRAY_OPCODE && evt->format == 32) {
|
||||||
m_logger.trace("tray: Received client_message");
|
m_log.trace("tray: Received client_message");
|
||||||
|
|
||||||
switch (evt->data.data32[1]) {
|
switch (evt->data.data32[1]) {
|
||||||
case SYSTEM_TRAY_REQUEST_DOCK:
|
case SYSTEM_TRAY_REQUEST_DOCK:
|
||||||
@ -560,7 +566,7 @@ class traymanager
|
|||||||
process_docking_request(evt->data.data32[2]);
|
process_docking_request(evt->data.data32[2]);
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
auto id = m_connection.id(evt->data.data32[2]);
|
auto id = m_connection.id(evt->data.data32[2]);
|
||||||
m_logger.err("Error while processing docking request for %s (%s)", id, err.what());
|
m_log.err("Error while processing docking request for %s (%s)", id, err.what());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -574,9 +580,9 @@ class traymanager
|
|||||||
}
|
}
|
||||||
} else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW) {
|
} else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW) {
|
||||||
if (evt->window == m_tray) {
|
if (evt->window == m_tray) {
|
||||||
m_logger.info("Received WM_DELETE; removing system tray");
|
m_log.warn("Received WM_DELETE");
|
||||||
m_logger.err("FIXME: disabled...");
|
m_tray = XCB_NONE;
|
||||||
std::exit(EXIT_FAILURE);
|
deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,10 +595,12 @@ class traymanager
|
|||||||
* so we return an answer that'll put him in place.
|
* so we return an answer that'll put him in place.
|
||||||
*/
|
*/
|
||||||
void handle(const evt::configure_request& evt) {
|
void handle(const evt::configure_request& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
|
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
if (client) {
|
if (client) {
|
||||||
m_logger.trace(
|
m_log.trace("tray: Received configure_request for client %s", m_connection.id(evt->window));
|
||||||
"tray: Received configure_request for client %s", m_connection.id(evt->window));
|
|
||||||
client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing,
|
client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing,
|
||||||
m_settings.width, m_settings.height);
|
m_settings.width, m_settings.height);
|
||||||
}
|
}
|
||||||
@ -602,9 +610,12 @@ class traymanager
|
|||||||
* @see tray_manager::handle(const evt::configure_request&);
|
* @see tray_manager::handle(const evt::configure_request&);
|
||||||
*/
|
*/
|
||||||
void handle(const evt::resize_request& evt) {
|
void handle(const evt::resize_request& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
|
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
if (client) {
|
if (client) {
|
||||||
m_logger.trace("tray: Received resize_request for client %s", m_connection.id(evt->window));
|
m_log.trace("tray: Received resize_request for client %s", m_connection.id(evt->window));
|
||||||
client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing,
|
client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing,
|
||||||
m_settings.width, m_settings.height);
|
m_settings.width, m_settings.height);
|
||||||
}
|
}
|
||||||
@ -614,27 +625,35 @@ class traymanager
|
|||||||
* Event callback : XCB_SELECTION_CLEAR
|
* Event callback : XCB_SELECTION_CLEAR
|
||||||
*/
|
*/
|
||||||
void handle(const evt::selection_clear& evt) {
|
void handle(const evt::selection_clear& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
if (evt->selection != m_atom)
|
if (evt->selection != m_atom)
|
||||||
return;
|
return;
|
||||||
|
if (evt->owner != m_tray)
|
||||||
|
return;
|
||||||
|
|
||||||
m_logger.trace("tray: Received selection_clear");
|
try {
|
||||||
|
m_log.warn("Lost systray selection, deactivating...");
|
||||||
if (m_activated && evt->owner == m_tray) {
|
m_othermanager = m_connection.get_selection_owner(m_atom)->owner;
|
||||||
m_logger.warn("Lost systray selection, deactivating...");
|
track_selection_owner(m_othermanager);
|
||||||
m_othermanager = m_connection.get_selection_owner_unchecked(m_atom)->owner;
|
} catch (const std::exception& err) {
|
||||||
// track_selection_owner();
|
m_log.err("Failed to get systray selection owner");
|
||||||
deactivate();
|
m_othermanager = XCB_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event callback : XCB_PROPERTY_NOTIFY
|
* Event callback : XCB_PROPERTY_NOTIFY
|
||||||
*/
|
*/
|
||||||
void handle(const evt::property_notify& evt) {
|
void handle(const evt::property_notify& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
if (evt->atom != _XEMBED_INFO)
|
if (evt->atom != _XEMBED_INFO)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_logger.trace("tray: _XEMBED_INFO: %s", m_connection.id(evt->window));
|
m_log.trace("tray: _XEMBED_INFO: %s", m_connection.id(evt->window));
|
||||||
|
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
if (!client)
|
if (!client)
|
||||||
@ -644,16 +663,16 @@ class traymanager
|
|||||||
auto win = client->window();
|
auto win = client->window();
|
||||||
|
|
||||||
if (evt->state == XCB_PROPERTY_NEW_VALUE)
|
if (evt->state == XCB_PROPERTY_NEW_VALUE)
|
||||||
m_logger.trace("tray: _XEMBED_INFO value has changed");
|
m_log.trace("tray: _XEMBED_INFO value has changed");
|
||||||
|
|
||||||
xembed::query(m_connection, win, xd);
|
xembed::query(m_connection, win, xd);
|
||||||
m_logger.trace("tray: _XEMBED_INFO[0]=%u _XEMBED_INFO[1]=%u", xd->version, xd->flags);
|
m_log.trace("tray: _XEMBED_INFO[0]=%u _XEMBED_INFO[1]=%u", xd->version, xd->flags);
|
||||||
|
|
||||||
if (!client->mapped() && ((xd->flags & XEMBED_MAPPED) == XEMBED_MAPPED)) {
|
if (!client->mapped() && ((xd->flags & XEMBED_MAPPED) == XEMBED_MAPPED)) {
|
||||||
m_logger.info("tray: Map client window: %s", m_connection.id(win));
|
m_log.info("tray: Map client window: %s", m_connection.id(win));
|
||||||
m_connection.map_window(win);
|
m_connection.map_window(win);
|
||||||
} else if (client->mapped() && ((xd->flags & XEMBED_MAPPED) != XEMBED_MAPPED)) {
|
} else if (client->mapped() && ((xd->flags & XEMBED_MAPPED) != XEMBED_MAPPED)) {
|
||||||
m_logger.info("tray: Unmap client window: %s", m_connection.id(win));
|
m_log.info("tray: Unmap client window: %s", m_connection.id(win));
|
||||||
m_connection.unmap_window(win);
|
m_connection.unmap_window(win);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -662,14 +681,16 @@ class traymanager
|
|||||||
* Event callback : XCB_REPARENT_NOTIFY
|
* Event callback : XCB_REPARENT_NOTIFY
|
||||||
*/
|
*/
|
||||||
void handle(const evt::reparent_notify& evt) {
|
void handle(const evt::reparent_notify& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
if (evt->parent == m_tray)
|
if (evt->parent == m_tray)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
|
|
||||||
if (client) {
|
if (client) {
|
||||||
m_logger.trace("tray: Received reparent_notify");
|
m_log.trace("tray: Received reparent_notify");
|
||||||
m_logger.trace("tray: Remove tray client");
|
m_log.trace("tray: Remove tray client");
|
||||||
m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client));
|
m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client));
|
||||||
reconfigure();
|
reconfigure();
|
||||||
}
|
}
|
||||||
@ -679,36 +700,41 @@ class traymanager
|
|||||||
* Event callback : XCB_DESTROY_NOTIFY
|
* Event callback : XCB_DESTROY_NOTIFY
|
||||||
*/
|
*/
|
||||||
void handle(const evt::destroy_notify& evt) {
|
void handle(const evt::destroy_notify& evt) {
|
||||||
|
if (!m_activated && evt->window == m_othermanager) {
|
||||||
|
m_log.trace("tray: Received destroy_notify");
|
||||||
|
m_log.trace("tray: Systray selection is available... re-activating");
|
||||||
|
activate();
|
||||||
|
} else if (m_activated) {
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
|
|
||||||
if (!m_activated && evt->window == m_othermanager) {
|
if (client) {
|
||||||
m_logger.trace("tray: Received destroy_notify");
|
m_log.trace("tray: Received destroy_notify");
|
||||||
m_logger.trace("tray: Systray selection is available... activating");
|
m_log.trace("tray: Remove tray client");
|
||||||
activate();
|
|
||||||
} else if (client) {
|
|
||||||
m_logger.trace("tray: Received destroy_notify");
|
|
||||||
m_logger.trace("tray: Remove tray client");
|
|
||||||
m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client));
|
m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client));
|
||||||
reconfigure();
|
reconfigure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event callback : XCB_MAP_NOTIFY
|
* Event callback : XCB_MAP_NOTIFY
|
||||||
*/
|
*/
|
||||||
void handle(const evt::map_notify& evt) {
|
void handle(const evt::map_notify& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
|
|
||||||
if (evt->window == m_tray && !m_mapped) {
|
if (evt->window == m_tray && !m_mapped) {
|
||||||
m_logger.trace("tray: Received map_notify");
|
m_log.trace("tray: Received map_notify");
|
||||||
if (m_mapped)
|
if (m_mapped)
|
||||||
return;
|
return;
|
||||||
m_logger.trace("tray: Update container mapped flag");
|
m_log.trace("tray: Update container mapped flag");
|
||||||
m_mapped = true;
|
m_mapped = true;
|
||||||
reconfigure();
|
reconfigure();
|
||||||
} else {
|
} else {
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
if (client) {
|
if (client) {
|
||||||
m_logger.trace("tray: Received map_notify");
|
m_log.trace("tray: Received map_notify");
|
||||||
m_logger.trace("tray: Set client mapped");
|
m_log.trace("tray: Set client mapped");
|
||||||
client->mapped(true);
|
client->mapped(true);
|
||||||
reconfigure();
|
reconfigure();
|
||||||
}
|
}
|
||||||
@ -719,19 +745,22 @@ class traymanager
|
|||||||
* Event callback : XCB_UNMAP_NOTIFY
|
* Event callback : XCB_UNMAP_NOTIFY
|
||||||
*/
|
*/
|
||||||
void handle(const evt::unmap_notify& evt) {
|
void handle(const evt::unmap_notify& evt) {
|
||||||
|
if (!m_activated)
|
||||||
|
return;
|
||||||
|
|
||||||
if (evt->window == m_tray) {
|
if (evt->window == m_tray) {
|
||||||
m_logger.trace("tray: Received unmap_notify");
|
m_log.trace("tray: Received unmap_notify");
|
||||||
if (!m_mapped)
|
if (!m_mapped)
|
||||||
return;
|
return;
|
||||||
m_logger.trace("tray: Update container mapped flag");
|
m_log.trace("tray: Update container mapped flag");
|
||||||
m_mapped = false;
|
m_mapped = false;
|
||||||
reconfigure();
|
reconfigure();
|
||||||
} else {
|
} else {
|
||||||
auto client = find_client(evt->window);
|
auto client = find_client(evt->window);
|
||||||
|
|
||||||
if (client) {
|
if (client) {
|
||||||
m_logger.trace("tray: Received unmap_notify");
|
m_log.trace("tray: Received unmap_notify");
|
||||||
m_logger.trace("tray: Set client unmapped");
|
m_log.trace("tray: Set client unmapped");
|
||||||
client->mapped(true);
|
client->mapped(true);
|
||||||
reconfigure();
|
reconfigure();
|
||||||
}
|
}
|
||||||
@ -740,7 +769,7 @@ class traymanager
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
connection& m_connection;
|
connection& m_connection;
|
||||||
const logger& m_logger;
|
const logger& m_log;
|
||||||
vector<shared_ptr<trayclient>> m_clients;
|
vector<shared_ptr<trayclient>> m_clients;
|
||||||
|
|
||||||
tray_settings m_settings;
|
tray_settings m_settings;
|
||||||
@ -755,7 +784,7 @@ class traymanager
|
|||||||
stateflag m_hidden{false};
|
stateflag m_hidden{false};
|
||||||
stateflag m_sinkattached{false};
|
stateflag m_sinkattached{false};
|
||||||
|
|
||||||
thread m_notifythread;
|
thread m_delayed_activation;
|
||||||
|
|
||||||
bool m_restacked = false;
|
bool m_restacked = false;
|
||||||
};
|
};
|
||||||
|
@ -80,7 +80,8 @@ namespace xembed {
|
|||||||
/**
|
/**
|
||||||
* Acknowledge window embedding
|
* Acknowledge window embedding
|
||||||
*/
|
*/
|
||||||
inline auto notify_embedded(connection& conn, xcb_window_t win, xcb_window_t embedder, long version) {
|
inline auto notify_embedded(
|
||||||
|
connection& conn, xcb_window_t win, xcb_window_t embedder, long version) {
|
||||||
send_message(conn, win, XEMBED_EMBEDDED_NOTIFY, 0, embedder, version);
|
send_message(conn, win, XEMBED_EMBEDDED_NOTIFY, 0, embedder, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,8 +117,12 @@ namespace xembed {
|
|||||||
* Unembed given window
|
* Unembed given window
|
||||||
*/
|
*/
|
||||||
inline auto unembed(connection& conn, xcb_window_t win, xcb_window_t root) {
|
inline auto unembed(connection& conn, xcb_window_t win, xcb_window_t root) {
|
||||||
conn.unmap_window(win);
|
try {
|
||||||
conn.reparent_window(win, root, 0, 0);
|
conn.unmap_window_checked(win);
|
||||||
|
conn.reparent_window_checked(win, root, 0, 0);
|
||||||
|
} catch (const xpp::x::error::window& err) {
|
||||||
|
// invalid window
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user