From 91a9bb6d95b0af81a929c48f6d850bea2ee1964c Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Tue, 25 Oct 2016 22:59:02 +0200 Subject: [PATCH] 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. --- CMakeLists.txt | 6 +- include/components/controller.hpp | 4 +- include/components/x11/connection.hpp | 3 +- include/components/x11/tray.hpp | 309 ++++++++++++++------------ include/components/x11/xembed.hpp | 11 +- 5 files changed, 184 insertions(+), 149 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57985c0a..249ca3d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,11 +137,11 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") message(STATUS " + relwithdebinfo flags:: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") endif() if(CXXLIB_CLANG) - message(STATUS "Linking C++ library: libc++") + message(STATUS " Linking C++ library: libc++") elseif(CXXLIB_GCC) - message(STATUS "Linking C++ library: libstdc++") + message(STATUS " Linking C++ library: libstdc++") else() - message(STATUS "Linking C++ library: system default") + message(STATUS " Linking C++ library: system default") endif() message(STATUS "---------------------------") message(STATUS " Build testsuite ${BUILD_TESTS}") diff --git a/include/components/controller.hpp b/include/components/controller.hpp index c28902ee..1443940d 100644 --- a/include/components/controller.hpp +++ b/include/components/controller.hpp @@ -84,6 +84,7 @@ class controller { if (m_traymanager) { m_log.trace("controller: Deactivate tray manager"); m_traymanager->deactivate(); + m_traymanager.reset(); } m_log.trace("controller: Deconstruct bar instance"); @@ -243,8 +244,7 @@ class controller { m_log.trace("controller: Listen for X events"); while (m_running) { auto evt = m_connection.wait_for_event(); - - if (evt != nullptr) + if (evt != nullptr && m_running) m_connection.dispatch_event(evt); } }); diff --git a/include/components/x11/connection.hpp b/include/components/x11/connection.hpp index 993fe733..027c2f0b 100644 --- a/include/components/x11/connection.hpp +++ b/include/components/x11/connection.hpp @@ -177,7 +177,8 @@ class connection : public xpp_connection { * Dispatch event through the registry */ void dispatch_event(const shared_ptr& evt) { - m_registry.dispatch(forward(evt)); + if (evt != nullptr) + m_registry.dispatch(evt); } protected: diff --git a/include/components/x11/tray.hpp b/include/components/x11/tray.hpp index 8825669b..99ed45dd 100644 --- a/include/components/x11/tray.hpp +++ b/include/components/x11/tray.hpp @@ -34,6 +34,10 @@ class trayclient { m_xembed->flags = XEMBED_MAPPED; } + ~trayclient() { + xembed::unembed(m_connection, window(), m_connection.root()); + } + /** * 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::reparent_notify, evt::destroy_notify, evt::map_notify, evt::unmap_notify> { public: - explicit traymanager(connection& conn, const logger& logger) - : m_connection(conn), m_logger(logger) { + explicit traymanager(connection& conn, const logger& logger) : m_connection(conn), m_log(logger) { m_connection.attach_sink(this, 2); m_sinkattached = true; } @@ -124,8 +127,6 @@ class traymanager auto bootstrap(tray_settings settings) { m_settings = settings; query_atom(); - create_window(); - set_wmhints(); } /** @@ -133,11 +134,22 @@ class traymanager */ void activate() { if (m_activated) { - // m_logger.warn("Tray is already activated..."); 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) { m_connection.attach_sink(this, 2); @@ -145,7 +157,7 @@ class traymanager } // 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 = bind(&traymanager::bar_visibility_change, this, std::placeholders::_1); } @@ -153,27 +165,15 @@ class traymanager // Attempt to get control of the systray selection then // notify clients waiting for a manager. acquire_selection(); + + // If replacing an existing manager or if re-activating from getting + // replaced, we delay the notification broadcast to allow the clients + // to get unembedded... + if (m_othermanager != XCB_NONE) + this_thread::sleep_for(1s); notify_clients(); - m_activated = true; m_connection.flush(); - - // Send delayed notifications - // 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); - try { - if (!m_activated) - break; - notify_clients(); - m_connection.flush(); - } catch (...) { - } - } - }); } /** @@ -181,40 +181,46 @@ class traymanager */ void deactivate() { if (!m_activated) { - // m_logger.warn("Tray is already deactivated..."); return; } - m_logger.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_log.info("Deactivating traymanager"); m_activated = false; - if (m_notifythread.joinable()) - m_notifythread.join(); + if (m_delayed_activation.joinable()) + m_delayed_activation.join(); - try { - if (m_connection.get_selection_owner(m_atom).owner() == m_tray) - m_connection.set_selection_owner_checked(XCB_NONE, m_atom, XCB_CURRENT_TIME); - } catch (...) { + if (g_signals::tray::report_slotcount) { + m_log.trace("tray: Report empty slotcount"); + g_signals::tray::report_slotcount(0); + } + + 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() == 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_mapped = false; + } + + m_log.trace("tray: Destroy window"); + m_connection.destroy_window(m_tray); + m_tray = XCB_NONE; + m_hidden = false; } - m_connection.unmap_window(m_tray); m_connection.flush(); } @@ -295,7 +301,7 @@ class traymanager 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()); + m_log.warn("Failed to un-/map the tray window (%s)", err.what()); } } @@ -331,7 +337,7 @@ class traymanager * Find the systray selection 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())}; auto reply = m_connection.intern_atom(false, name.length(), name.c_str()); m_atom = reply.atom(); @@ -344,7 +350,7 @@ class traymanager auto x = calculate_x(); auto y = calculate_y(); 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); auto scr = m_connection.screen(); 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) { 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 */ 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_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 wm_flags; wm_flags.emplace_back(WM_DELETE_WINDOW); wm_flags.emplace_back(WM_TAKE_FOCUS); xcb_icccm_set_wm_protocols( 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 types; types.emplace_back(_NET_WM_WINDOW_TYPE_DOCK); 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, 32, types.size(), types.data()); - m_logger.trace("tray: Set window _NET_WM_STATE"); + m_log.trace("tray: Set window _NET_WM_STATE"); vector states; 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, 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}; m_connection.change_property_checked(XCB_PROP_MODE_REPLACE, m_tray, _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(); m_connection.change_property( 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; if (owner == m_tray) { - m_logger.info("tray: Already managing the systray selection"); + m_log.info("tray: Already managing the systray selection"); return; - } else if (owner != XCB_NONE) { - m_logger.info("tray: Replace existing selection manager %s", m_connection.id(owner)); - // track_selection_owner(owner); + } else if ((m_othermanager = owner) != XCB_NONE) { + m_log.info("Replacing selection manager %s", m_connection.id(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); if (m_connection.get_selection_owner_unchecked(m_atom)->owner != m_tray) throw application_error("Failed to get control of the systray selection"); - - m_othermanager = XCB_NONE; } /** * Notify pending clients about the new systray MANAGER */ 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()); message->data.data32[0] = XCB_CURRENT_TIME; message->data.data32[1] = m_atom; @@ -447,21 +450,17 @@ class traymanager * Track changes to the given selection owner * If it gets destroyed or goes away we can reactivate the traymanager */ - // void track_selection_owner(xcb_window_t owner = XCB_NONE) { - // try { - // if (owner != XCB_NONE) { - // m_othermanager = owner; - // } else { - // m_othermanager = m_connection.get_selection_owner_unchecked(m_atom)->owner; - // } - // m_logger.trace("tray: Listen for events on the new selection window"); - // const uint32_t event_mask[1]{XCB_EVENT_MASK_STRUCTURE_NOTIFY}; - // 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"); - // } - // } + void track_selection_owner(xcb_window_t owner) { + try { + if (owner == XCB_NONE) + return; + m_log.trace("tray: Listen for events on the new selection window"); + const uint32_t event_mask[1]{XCB_EVENT_MASK_STRUCTURE_NOTIFY}; + m_connection.change_window_attributes_checked(owner, XCB_CW_EVENT_MASK, event_mask); + } catch (const xpp::x::error::window& err) { + m_log.err("Failed to track selection owner"); + } + } /** * Calculates a client's x position @@ -480,13 +479,13 @@ class traymanager auto client = find_client(win); if (client) { 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 { - m_logger.trace("tray: Refresh _XEMBED_INFO"); + m_log.trace("tray: Refresh _XEMBED_INFO"); xembed::query(m_connection, win, client->xembed()); 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()); } } @@ -494,36 +493,36 @@ class traymanager 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(m_connection, win)); client = m_clients.back(); try { - m_logger.trace("tray: Get client _XEMBED_INFO"); + m_log.trace("tray: Get client _XEMBED_INFO"); xembed::query(m_connection, win, client->xembed()); } 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_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}; 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_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}; m_connection.configure_window( 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); - m_logger.trace("tray: Map tray client"); + m_log.trace("tray: Map tray client"); m_connection.map_window(client->window()); } @@ -531,9 +530,11 @@ class traymanager * Event callback : XCB_EXPOSE */ void handle(const evt::expose& evt) { - if (!m_activated || m_clients.empty()) + if (!m_activated) 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(); } @@ -543,7 +544,9 @@ class traymanager void handle(const evt::visibility_notify& evt) { if (!m_activated || m_clients.empty()) 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(); } @@ -551,8 +554,11 @@ class traymanager * Event callback : XCB_CLIENT_MESSAGE */ void handle(const evt::client_message& evt) { + if (!m_activated) + return; + 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]) { case SYSTEM_TRAY_REQUEST_DOCK: @@ -560,7 +566,7 @@ class traymanager process_docking_request(evt->data.data32[2]); } catch (const std::exception& err) { 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; @@ -574,9 +580,9 @@ class traymanager } } else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW) { if (evt->window == m_tray) { - m_logger.info("Received WM_DELETE; removing system tray"); - m_logger.err("FIXME: disabled..."); - std::exit(EXIT_FAILURE); + m_log.warn("Received WM_DELETE"); + m_tray = XCB_NONE; + deactivate(); } } } @@ -589,10 +595,12 @@ class traymanager * so we return an answer that'll put him in place. */ void handle(const evt::configure_request& evt) { + if (!m_activated) + return; + auto client = find_client(evt->window); if (client) { - m_logger.trace( - "tray: Received configure_request for client %s", m_connection.id(evt->window)); + m_log.trace("tray: Received configure_request for client %s", m_connection.id(evt->window)); client->configure_notify(calculate_client_xpos(evt->window), m_settings.spacing, m_settings.width, m_settings.height); } @@ -602,9 +610,12 @@ class traymanager * @see tray_manager::handle(const evt::configure_request&); */ void handle(const evt::resize_request& evt) { + if (!m_activated) + return; + auto client = find_client(evt->window); 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, m_settings.width, m_settings.height); } @@ -614,27 +625,35 @@ class traymanager * Event callback : XCB_SELECTION_CLEAR */ void handle(const evt::selection_clear& evt) { + if (!m_activated) + return; if (evt->selection != m_atom) return; + if (evt->owner != m_tray) + return; - m_logger.trace("tray: Received selection_clear"); - - if (m_activated && evt->owner == m_tray) { - m_logger.warn("Lost systray selection, deactivating..."); - m_othermanager = m_connection.get_selection_owner_unchecked(m_atom)->owner; - // track_selection_owner(); - deactivate(); + try { + m_log.warn("Lost systray selection, deactivating..."); + m_othermanager = m_connection.get_selection_owner(m_atom)->owner; + track_selection_owner(m_othermanager); + } catch (const std::exception& err) { + m_log.err("Failed to get systray selection owner"); + m_othermanager = XCB_NONE; } + + deactivate(); } /** * Event callback : XCB_PROPERTY_NOTIFY */ void handle(const evt::property_notify& evt) { + if (!m_activated) + return; if (evt->atom != _XEMBED_INFO) 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); if (!client) @@ -644,16 +663,16 @@ class traymanager auto win = client->window(); 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); - 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)) { - 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); } 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); } } @@ -662,14 +681,16 @@ class traymanager * Event callback : XCB_REPARENT_NOTIFY */ void handle(const evt::reparent_notify& evt) { + if (!m_activated) + return; if (evt->parent == m_tray) return; auto client = find_client(evt->window); if (client) { - m_logger.trace("tray: Received reparent_notify"); - m_logger.trace("tray: Remove tray client"); + m_log.trace("tray: Received reparent_notify"); + m_log.trace("tray: Remove tray client"); m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client)); reconfigure(); } @@ -679,17 +700,19 @@ class traymanager * Event callback : XCB_DESTROY_NOTIFY */ void handle(const evt::destroy_notify& evt) { - auto client = find_client(evt->window); - if (!m_activated && evt->window == m_othermanager) { - m_logger.trace("tray: Received destroy_notify"); - m_logger.trace("tray: Systray selection is available... activating"); + m_log.trace("tray: Received destroy_notify"); + m_log.trace("tray: Systray selection is available... re-activating"); 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)); - reconfigure(); + } else if (m_activated) { + auto client = find_client(evt->window); + + if (client) { + m_log.trace("tray: Received destroy_notify"); + m_log.trace("tray: Remove tray client"); + m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client)); + reconfigure(); + } } } @@ -697,18 +720,21 @@ class traymanager * Event callback : XCB_MAP_NOTIFY */ void handle(const evt::map_notify& evt) { + if (!m_activated) + return; + if (evt->window == m_tray && !m_mapped) { - m_logger.trace("tray: Received map_notify"); + m_log.trace("tray: Received map_notify"); if (m_mapped) return; - m_logger.trace("tray: Update container mapped flag"); + m_log.trace("tray: Update container mapped flag"); m_mapped = true; reconfigure(); } else { auto client = find_client(evt->window); if (client) { - m_logger.trace("tray: Received map_notify"); - m_logger.trace("tray: Set client mapped"); + m_log.trace("tray: Received map_notify"); + m_log.trace("tray: Set client mapped"); client->mapped(true); reconfigure(); } @@ -719,19 +745,22 @@ class traymanager * Event callback : XCB_UNMAP_NOTIFY */ void handle(const evt::unmap_notify& evt) { + if (!m_activated) + return; + if (evt->window == m_tray) { - m_logger.trace("tray: Received unmap_notify"); + m_log.trace("tray: Received unmap_notify"); if (!m_mapped) return; - m_logger.trace("tray: Update container mapped flag"); + m_log.trace("tray: Update container mapped flag"); m_mapped = false; reconfigure(); } else { auto client = find_client(evt->window); if (client) { - m_logger.trace("tray: Received unmap_notify"); - m_logger.trace("tray: Set client unmapped"); + m_log.trace("tray: Received unmap_notify"); + m_log.trace("tray: Set client unmapped"); client->mapped(true); reconfigure(); } @@ -740,7 +769,7 @@ class traymanager private: connection& m_connection; - const logger& m_logger; + const logger& m_log; vector> m_clients; tray_settings m_settings; @@ -755,7 +784,7 @@ class traymanager stateflag m_hidden{false}; stateflag m_sinkattached{false}; - thread m_notifythread; + thread m_delayed_activation; bool m_restacked = false; }; diff --git a/include/components/x11/xembed.hpp b/include/components/x11/xembed.hpp index 1b2bee5d..b2ed0b9f 100644 --- a/include/components/x11/xembed.hpp +++ b/include/components/x11/xembed.hpp @@ -80,7 +80,8 @@ namespace xembed { /** * 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); } @@ -116,8 +117,12 @@ namespace xembed { * Unembed given window */ inline auto unembed(connection& conn, xcb_window_t win, xcb_window_t root) { - conn.unmap_window(win); - conn.reparent_window(win, root, 0, 0); + try { + conn.unmap_window_checked(win); + conn.reparent_window_checked(win, root, 0, 0); + } catch (const xpp::x::error::window& err) { + // invalid window + } } }