diff --git a/include/components/signals.hpp b/include/components/signals.hpp index 9b3ca2b5..217c2f82 100644 --- a/include/components/signals.hpp +++ b/include/components/signals.hpp @@ -12,6 +12,7 @@ namespace g_signals { namespace bar { extern callback action_click; extern callback visibility_change; + extern callback<> redraw; } namespace parser { diff --git a/include/components/types.hpp b/include/components/types.hpp index 792225ba..b20316c8 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -61,31 +61,24 @@ struct bar_settings { struct tray_settings { tray_settings() = default; - - tray_settings& operator=(tray_settings& o) { - align = o.align; - orig_x = o.orig_x; - orig_y = o.orig_y; - width = o.width; - height = o.height; - height_fill = o.height_fill; - spacing = o.spacing; - slots = o.slots; - sibling = o.sibling; - background = o.background; - return *this; - } + tray_settings& operator=(const tray_settings& o) = default; alignment align{alignment::NONE}; int16_t orig_x{0}; int16_t orig_y{0}; + int16_t configured_x{0}; + int16_t configured_y{0}; + uint16_t configured_w{0}; + uint16_t configured_h{0}; + uint16_t configured_slots{0}; uint16_t width{0}; uint16_t height{0}; uint16_t height_fill{0}; uint16_t spacing{0}; - uint16_t slots{0}; uint32_t sibling{0}; uint32_t background{0}; + xcb_pixmap_t back_pixmap{0}; + bool transparent{false}; }; struct border_settings { diff --git a/include/x11/atoms.hpp b/include/x11/atoms.hpp index 944e2084..a688d8fe 100644 --- a/include/x11/atoms.hpp +++ b/include/x11/atoms.hpp @@ -8,7 +8,7 @@ struct cached_atom { xcb_atom_t* atom; }; -extern cached_atom ATOMS[26]; +extern cached_atom ATOMS[29]; extern xcb_atom_t _NET_WM_NAME; extern xcb_atom_t _NET_WM_DESKTOP; @@ -36,3 +36,6 @@ extern xcb_atom_t _NET_SYSTEM_TRAY_COLORS; extern xcb_atom_t WM_TAKE_FOCUS; extern xcb_atom_t Backlight; extern xcb_atom_t BACKLIGHT; +extern xcb_atom_t _XROOTMAP_ID; +extern xcb_atom_t _XSETROOT_ID; +extern xcb_atom_t ESETROOT_PMAP_ID; diff --git a/include/x11/graphics.hpp b/include/x11/graphics.hpp new file mode 100644 index 00000000..013abce4 --- /dev/null +++ b/include/x11/graphics.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "common.hpp" + +LEMONBUDDY_NS + +class connection; + +namespace graphics_util { + struct root_pixmap { + uint8_t depth{0}; + uint16_t width{0}; + uint16_t height{0}; + int16_t x{0}; + int16_t y{0}; + xcb_pixmap_t pixmap{0}; + }; + + void get_root_pixmap(connection& conn, root_pixmap* rpix); + + void simple_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc); + void simple_pixmap(connection& conn, xcb_window_t dst, int w, int h, xcb_pixmap_t* pixmap); +} + +LEMONBUDDY_NS_END diff --git a/include/x11/tray.hpp b/include/x11/tray.hpp index f8ea25ea..99f68f87 100644 --- a/include/x11/tray.hpp +++ b/include/x11/tray.hpp @@ -11,6 +11,7 @@ #include "utils/memory.hpp" #include "utils/process.hpp" #include "x11/connection.hpp" +#include "x11/graphics.hpp" #include "x11/xembed.hpp" #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 @@ -75,6 +76,8 @@ class traymanager protected: void reconfigure_window(); void reconfigure_clients(); + void reconfigure_bg(bool realloc = false); + void refresh_window(); void query_atom(); void create_window(); @@ -89,7 +92,7 @@ class traymanager void bar_visibility_change(bool state); - int16_t calculate_x(uint32_t width) const; + int16_t calculate_x(uint16_t width) const; int16_t calculate_y() const; uint16_t calculate_w() const; uint16_t calculate_h() const; @@ -120,6 +123,12 @@ class traymanager tray_settings m_settings; + xcb_gcontext_t m_gc{0}; + xcb_pixmap_t m_pixmap{0}; + graphics_util::root_pixmap m_rootpixmap; + uint16_t m_prevwidth{0}; + uint16_t m_prevheight{0}; + xcb_atom_t m_atom{0}; xcb_window_t m_tray{0}; xcb_window_t m_othermanager{0}; diff --git a/src/x11/atoms.cpp b/src/x11/atoms.cpp index 9b669a17..358c2655 100644 --- a/src/x11/atoms.cpp +++ b/src/x11/atoms.cpp @@ -29,9 +29,12 @@ xcb_atom_t _NET_SYSTEM_TRAY_COLORS; xcb_atom_t WM_TAKE_FOCUS; xcb_atom_t Backlight; xcb_atom_t BACKLIGHT; +xcb_atom_t _XROOTMAP_ID; +xcb_atom_t _XSETROOT_ID; +xcb_atom_t ESETROOT_PMAP_ID; // clang-format off -cached_atom ATOMS[26] = { +cached_atom ATOMS[29] = { {"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &_NET_WM_NAME}, {"_NET_WM_DESKTOP", sizeof("_NET_WM_DESKTOP") - 1, &_NET_WM_DESKTOP}, {"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1, &_NET_WM_WINDOW_TYPE}, @@ -58,5 +61,8 @@ cached_atom ATOMS[26] = { {"WM_TAKE_FOCUS", sizeof("WM_TAKE_FOCUS") - 1, &WM_TAKE_FOCUS}, {"Backlight", sizeof("Backlight") - 1, &Backlight}, {"BACKLIGHT", sizeof("BACKLIGHT") - 1, &BACKLIGHT}, + {"_XROOTMAP_ID", sizeof("_XROOTMAP_ID") - 1, &_XROOTMAP_ID}, + {"_XSETROOT_ID", sizeof("_XSETROOT_ID") - 1, &_XSETROOT_ID}, + {"ESETROOT_PMAP_ID", sizeof("ESETROOT_PMAP_ID") - 1, &ESETROOT_PMAP_ID}, }; // clang-format on diff --git a/src/x11/graphics.cpp b/src/x11/graphics.cpp new file mode 100644 index 00000000..1f91f17d --- /dev/null +++ b/src/x11/graphics.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "x11/atoms.hpp" +#include "x11/connection.hpp" +#include "x11/graphics.hpp" + +LEMONBUDDY_NS + +namespace graphics_util { + /** + * Query for the root window pixmap + */ + void get_root_pixmap(connection& conn, root_pixmap* rpix) { + auto screen = conn.screen(); + const xcb_atom_t pixmap_properties[3]{ESETROOT_PMAP_ID, _XROOTMAP_ID, _XSETROOT_ID}; + for (auto&& property : pixmap_properties) { + auto cookie = xcb_get_property(conn, false, screen->root, property, XCB_ATOM_PIXMAP, 0L, 1L); + auto reply = xcb_get_property_reply(conn, cookie, nullptr); + + if (reply && reply->format == 32 && reply->value_len == 1) { + rpix->pixmap = *static_cast(xcb_get_property_value(reply)); + } + } + + if (!rpix->pixmap) { + return; + } + + auto cookie = xcb_get_geometry(conn, rpix->pixmap); + auto reply = xcb_get_geometry_reply(conn, cookie, nullptr); + + if (!reply) { + rpix->pixmap = 0; + return; + } + + rpix->depth = reply->depth; + rpix->width = reply->width; + rpix->height = reply->height; + rpix->x = reply->x; + rpix->y = reply->y; + } + + /** + * Create a basic gc + */ + void simple_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc) { + xcb_params_gc_t params; + + uint32_t mask = 0; + uint32_t values[32]; + + XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, false); + xutils::pack_values(mask, ¶ms, values); + + try { + *gc = conn.generate_id(); + conn.create_gc_checked(*gc, drawable, mask, values); + } catch (const std::exception& err) { + // no-op + } + } + + /** + * Create a basic pixmap + */ + void simple_pixmap(connection& conn, xcb_window_t dst, int w, int h, xcb_pixmap_t* pixmap) { + try { + *pixmap = conn.generate_id(); + conn.create_pixmap_checked(conn.screen()->root_depth, *pixmap, dst, w, h); + } catch (const std::exception& err) { + // no-op + } + } +} + +LEMONBUDDY_NS_END diff --git a/src/x11/tray.cpp b/src/x11/tray.cpp index 5b78a013..42fe37c8 100644 --- a/src/x11/tray.cpp +++ b/src/x11/tray.cpp @@ -1,7 +1,10 @@ +#include + #include "x11/tray.hpp" #include "utils/color.hpp" #include "x11/draw.hpp" #include "x11/window.hpp" +#include "x11/wm.hpp" LEMONBUDDY_NS @@ -115,6 +118,10 @@ traymanager::~traymanager() { deactivate(); if (m_sinkattached) m_connection.detach_sink(this, 2); + if (m_pixmap) + xcb_free_pixmap(m_connection, m_pixmap); + if (m_gc) + xcb_free_gc(m_connection, m_gc); } /** @@ -159,6 +166,11 @@ void traymanager::activate() { // {{{ bind(&traymanager::bar_visibility_change, this, std::placeholders::_1); } + // Listen for redraw events on the bar window + if (!g_signals::bar::redraw) { + g_signals::bar::redraw = bind(&traymanager::refresh_window, this); + } + // Attempt to get control of the systray selection then // notify clients waiting for a manager. acquire_selection(); @@ -168,6 +180,7 @@ void traymanager::activate() { // {{{ // to get unembedded... if (m_othermanager != XCB_NONE) this_thread::sleep_for(1s); + notify_clients(); m_connection.flush(); @@ -193,10 +206,13 @@ void traymanager::deactivate() { // {{{ } if (g_signals::bar::visibility_change) { - m_log.trace("tray: Clear callback handlers"); g_signals::bar::visibility_change = nullptr; } + if (g_signals::bar::redraw) { + g_signals::bar::redraw = nullptr; + } + 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); @@ -232,55 +248,100 @@ void traymanager::reconfigure() { // {{{ } if (!m_mtx.try_lock()) { - m_log.err("already locked"); return; } - std::lock_guard lock(m_mtx, std::adopt_lock); + std::unique_lock guard(m_mtx, std::adopt_lock); - reconfigure_clients(); - reconfigure_window(); + try { + reconfigure_clients(); + reconfigure_window(); + reconfigure_bg(); + refresh_window(); + m_connection.flush(); + } catch (const std::exception& err) { + m_log.err("Failed to reconfigure tray (%s)", err.what()); + return; + } m_connection.flush(); + m_settings.configured_slots = mapped_clients(); + // Report status if (g_signals::tray::report_slotcount) { - m_settings.slots = mapped_clients(); - g_signals::tray::report_slotcount(m_settings.slots); + g_signals::tray::report_slotcount(m_settings.configured_slots); } + + guard.unlock(); + + refresh_window(); } // }}} /** * Reconfigure container window */ void traymanager::reconfigure_window() { // {{{ + m_log.trace("tray: Reconfigure window"); + + if (m_tray == XCB_NONE) { + return; + } + auto clients = mapped_clients(); if (!clients && m_mapped) { - m_connection.unmap_window(m_tray); + m_log.trace("tray: Reconfigure window / unmap"); + m_mapped = false; + m_connection.unmap_window_checked(m_tray); } else if (clients && !m_mapped) { - m_connection.map_window(m_tray); - } else if (clients) { - // clear window to get rid of frozen artifacts - m_connection.clear_area(1, m_tray, 0, 0, 0, 0); - - // configure window - uint32_t mask = 0; - uint32_t values[7]; - xcb_params_configure_window_t params; - - XCB_AUX_ADD_PARAM(&mask, ¶ms, width, calculate_w()); - XCB_AUX_ADD_PARAM(&mask, ¶ms, x, calculate_x(params.width)); - - xutils::pack_values(mask, ¶ms, values); - m_connection.configure_window_checked(m_tray, mask, values); + m_log.trace("tray: Reconfigure window / map"); + m_mapped = true; + m_connection.map_window_checked(m_tray); } + + if (!m_mapped) { + m_log.trace("tray: Reconfigure window / ignoring unmapped"); + return; + } + + if (!clients) { + m_log.trace("tray: Reconfigure window / no clients"); + return; + } + + auto width = calculate_w(); + auto x = calculate_x(width); + + if (m_settings.configured_w == width && m_settings.configured_x == x) { + m_log.trace("tray: Reconfigure window / ignoring unchanged values w=%d x=%d", width, x); + return; + } + + // configure window + uint32_t mask = 0; + uint32_t values[7]; + xcb_params_configure_window_t params; + + m_log.trace("tray: New window values, width=%d, x=%d", width, x); + + XCB_AUX_ADD_PARAM(&mask, ¶ms, width, width); + XCB_AUX_ADD_PARAM(&mask, ¶ms, x, x); + + xutils::pack_values(mask, ¶ms, values); + + m_connection.configure_window_checked(m_tray, mask, values); + + m_settings.configured_w = width; + m_settings.configured_x = x; } // }}} /** * Reconfigure clients */ void traymanager::reconfigure_clients() { // {{{ + m_log.trace("tray: Reconfigure clients"); + uint32_t x = m_settings.spacing; for (auto it = m_clients.rbegin(); it != m_clients.rend(); it++) { @@ -297,6 +358,114 @@ void traymanager::reconfigure_clients() { // {{{ } } // }}} +/** + * Reconfigure root pixmap + */ +void traymanager::reconfigure_bg(bool realloc) { // {{{ + if (!m_settings.transparent) { + return; + } + + if (m_clients.empty() || !m_mapped) { + return; + } + + auto width = calculate_w(); + auto height = calculate_h(); + + if (!realloc) { + if (width == m_prevwidth && height == m_prevheight) { + return; + } else if (!width) { + return; + } + } + + m_log.trace("tray: Reconfigure bg"); + + if (!m_rootpixmap.pixmap || realloc) + graphics_util::get_root_pixmap(m_connection, &m_rootpixmap); + if (!m_pixmap) + graphics_util::simple_pixmap(m_connection, m_tray, width, height, &m_pixmap); + if (!m_gc) + graphics_util::simple_gc(m_connection, m_pixmap, &m_gc); + + if (!m_rootpixmap.pixmap) { + m_log.warn("Failed to get root pixmap for tray background"); + return; + } + + m_log.trace("tray: rootpixmap=%x (%dx%d+%d+%d), tray=%x, pixmap=%x, gc=%x", + m_rootpixmap.pixmap, + m_rootpixmap.width, + m_rootpixmap.height, + m_rootpixmap.x, + m_rootpixmap.y, + m_tray, m_pixmap, m_gc); + + m_prevwidth = width; + m_prevheight = height; + + auto x = calculate_x(width); + auto y = calculate_y(); + + m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_gc, x, y, 0, 0, width, height); + + auto image_reply = m_connection.get_image( + XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, 0, 0, width, height, XAllPlanes()); + + vector image_data; + std::back_insert_iterator back_it(image_data); + std::copy(image_reply.data().begin(), image_reply.data().end(), back_it); + uint8_t* data = image_data.data(); + + m_connection.put_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_gc, width, height, 0, 0, 0, + image_reply->depth, image_data.size(), data); + + m_connection.copy_area(m_rootpixmap.pixmap, m_pixmap, m_gc, x, y, 0, 0, width, height); + + uint32_t mask = 0; + uint32_t values[16]; + xcb_params_cw_t params; + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, m_pixmap); + xutils::pack_values(mask, ¶ms, values); + m_connection.change_window_attributes_checked(m_tray, mask, values); + + m_connection.copy_area(m_pixmap, m_tray, m_gc, x, y, 0, 0, width, height); +} // }}} + +/** + * Refresh the bar window by clearing and copying + * the root pixmap onto it + */ +void traymanager::refresh_window() { // {{{ + if (!m_mtx.try_lock()) { + return; + } + + std::lock_guard lock(m_mtx, std::adopt_lock); + + if (!m_activated || !m_mapped) { + return; + } + + m_log.trace("tray: Refreshing window"); + + auto width = calculate_w(); + auto height = calculate_h(); + auto x = calculate_x(width); + auto y = calculate_y(); + + m_connection.clear_area(1, m_tray, 0, 0, width, height); + + if (m_rootpixmap.pixmap && m_gc && m_pixmap) { + // draw_util::fill(m_connection, m_pixmap, m_gc, 0, 0, width, height); + m_connection.copy_area(m_pixmap, m_tray, m_gc, x, y, 0, 0, width, height); + } + + m_connection.flush(); +} // }}} + /** * Find the systray selection atom */ @@ -312,10 +481,10 @@ void traymanager::query_atom() { // {{{ */ void traymanager::create_window() { // {{{ auto scr = m_connection.screen(); - auto x = calculate_x(0); + auto w = calculate_w(); + auto h = calculate_h(); + auto x = calculate_x(w); auto y = calculate_y(); - auto w = m_settings.width + m_settings.spacing * 2; - auto h = m_settings.height_fill; m_tray = m_connection.generate_id(); m_log.trace("tray: Create tray window %s, (%ix%i+%i+%i)", m_connection.id(m_tray), w, h, x, y); @@ -324,8 +493,12 @@ void traymanager::create_window() { // {{{ uint32_t values[16]; xcb_params_cw_t params; - XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixel, m_settings.background); - XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, m_settings.background); + if (!m_settings.transparent) { + XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixel, m_settings.background); + XCB_AUX_ADD_PARAM(&mask, ¶ms, border_pixel, m_settings.background); + } + + XCB_AUX_ADD_PARAM(&mask, ¶ms, backing_store, XCB_BACKING_STORE_WHEN_MAPPED); XCB_AUX_ADD_PARAM(&mask, ¶ms, override_redirect, true); XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY); @@ -337,6 +510,8 @@ void traymanager::create_window() { // {{{ try { // Put the tray window above the defined sibling in the window stack if (m_settings.sibling != 0) { + m_log.trace("tray: Restacking tray window"); + uint32_t configure_mask = 0; uint32_t configure_values[7]; xcb_params_configure_window_t configure_params; @@ -367,14 +542,14 @@ void traymanager::set_wmhints() { // {{{ 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()); + wm_util::set_wmprotocols(m_connection, m_tray, wm_flags); 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()); + wm_util::set_wmprotocols(m_connection, m_tray, types); + wm_util::set_windowtype(m_connection, m_tray, types); m_log.trace("tray: Set window _NET_WM_STATE"); vector states; @@ -382,20 +557,14 @@ void traymanager::set_wmhints() { // {{{ m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_WM_STATE, XCB_ATOM_ATOM, 32, states.size(), states.data()); + m_log.trace("tray: Set window _NET_WM_PID"); + wm_util::set_wmpid(m_connection, m_tray, getpid()); + m_log.trace("tray: Set window _NET_SYSTEM_TRAY_ORIENTATION"); - const uint32_t values[1]{_NET_SYSTEM_TRAY_ORIENTATION_HORZ}; - m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_ORIENTATION, - _NET_SYSTEM_TRAY_ORIENTATION, 32, 1, values); + wm_util::set_trayorientation(m_connection, m_tray, _NET_SYSTEM_TRAY_ORIENTATION_HORZ); m_log.trace("tray: Set window _NET_SYSTEM_TRAY_VISUAL"); - const uint32_t values2[1]{m_connection.screen()->root_visual}; - m_connection.change_property( - XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_VISUAL, XCB_ATOM_VISUALID, 32, 1, values2); - - 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); + wm_util::set_trayvisual(m_connection, m_tray, m_connection.screen()->root_visual); } // }}} /** @@ -470,11 +639,12 @@ void traymanager::process_docking_request(xcb_window_t win) { // {{{ auto client = find_client(win); if (client) { - m_log.trace("tray: Client %s is already embedded, skipping...", m_connection.id(win)); + m_log.warn("Tray client %s already embedded, ignoring request...", m_connection.id(win)); return; } - m_log.trace("tray: Process docking request from %s", m_connection.id(win)); + m_log.info("Processing docking request from %s", m_connection.id(win)); + m_clients.emplace_back( make_shared(m_connection, win, m_settings.width, m_settings.height)); client = m_clients.back(); @@ -500,7 +670,7 @@ void traymanager::process_docking_request(xcb_window_t win) { // {{{ XCB_AUX_ADD_PARAM(&mask, ¶ms, event_mask, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY); - if (m_settings.background != 0) { + if (m_settings.transparent) { XCB_AUX_ADD_PARAM(&mask, ¶ms, back_pixmap, XCB_BACK_PIXMAP_PARENT_RELATIVE); } @@ -537,12 +707,12 @@ void traymanager::process_docking_request(xcb_window_t win) { // {{{ * toggle the tray window whenever the visibility of the bar window changes. */ void traymanager::bar_visibility_change(bool state) { // {{{ - // Ignore unchanged states if (m_hidden == !state) { return; } - // Update the psuedo-state + m_log.trace("tray: visibility_change %d", state); + m_hidden = !state; if (!m_hidden && !m_mapped) { @@ -559,7 +729,7 @@ void traymanager::bar_visibility_change(bool state) { // {{{ /** * Calculate x position of tray window */ -int16_t traymanager::calculate_x(uint32_t width) const { // {{{ +int16_t traymanager::calculate_x(uint16_t width) const { // {{{ auto x = m_settings.orig_x; if (m_settings.align == alignment::RIGHT) x -= ((m_settings.width + m_settings.spacing) * m_clients.size() + m_settings.spacing); @@ -579,7 +749,7 @@ int16_t traymanager::calculate_y() const { // {{{ * Calculate width of tray window */ uint16_t traymanager::calculate_w() const { // {{{ - uint32_t width = m_settings.spacing; + uint16_t width = m_settings.spacing; for (auto&& client : m_clients) { if (client->mapped()) { @@ -655,20 +825,20 @@ int traymanager::mapped_clients() const { // {{{ * Event callback : XCB_EXPOSE */ void traymanager::handle(const evt::expose& evt) { // {{{ - if (!m_activated || m_clients.empty()) - return; - m_log.trace("tray: Received expose event for %s", m_connection.id(evt->window)); - reconfigure(); + if (m_activated && !m_clients.empty()) { + m_log.trace("tray: Received expose event for %s", m_connection.id(evt->window)); + reconfigure_window(); + } } // }}} /** * Event callback : XCB_VISIBILITY_NOTIFY */ void traymanager::handle(const evt::visibility_notify& evt) { // {{{ - if (!m_activated || m_clients.empty()) - return; - m_log.trace("tray: Received visibility_notify for %s", m_connection.id(evt->window)); - reconfigure(); + if (m_activated && !m_clients.empty()) { + m_log.trace("tray: Received visibility_notify for %s", m_connection.id(evt->window)); + reconfigure_window(); + } } // }}} /** @@ -717,13 +887,14 @@ void traymanager::handle(const evt::client_message& evt) { // {{{ * so we return an answer that'll put him in place. */ void traymanager::handle(const evt::configure_request& evt) { // {{{ - if (!m_activated) + if (!m_activated) { return; + } auto client = find_client(evt->window); - - if (!client) + if (!client) { return; + } try { m_log.trace("tray: Client configure request %s", m_connection.id(evt->window)); @@ -738,13 +909,14 @@ void traymanager::handle(const evt::configure_request& evt) { // {{{ * @see tray_manager::handle(const evt::configure_request&); */ void traymanager::handle(const evt::resize_request& evt) { // {{{ - if (!m_activated) + if (!m_activated) { return; + } auto client = find_client(evt->window); - - if (!client) + if (!client) { return; + } try { m_log.trace("tray: Received resize_request for client %s", m_connection.id(evt->window)); @@ -759,12 +931,13 @@ void traymanager::handle(const evt::resize_request& evt) { // {{{ * Event callback : XCB_SELECTION_CLEAR */ void traymanager::handle(const evt::selection_clear& evt) { // {{{ - if (!m_activated) + if (!m_activated) { return; - if (evt->selection != m_atom) + } else if (evt->selection != m_atom) { return; - if (evt->owner != m_tray) + } else if (evt->owner != m_tray) { return; + } try { m_log.warn("Lost systray selection, deactivating..."); @@ -782,39 +955,48 @@ void traymanager::handle(const evt::selection_clear& evt) { // {{{ * Event callback : XCB_PROPERTY_NOTIFY */ void traymanager::handle(const evt::property_notify& evt) { // {{{ - if (!m_activated) - return; - if (evt->atom != _XEMBED_INFO) + if (!m_activated) { return; + } else if (evt->atom == _XROOTMAP_ID) { + reconfigure_bg(true); + } else if (evt->atom == _XSETROOT_ID) { + reconfigure_bg(true); + } else if (evt->atom == ESETROOT_PMAP_ID) { + reconfigure_bg(true); + } else if (evt->atom != _XEMBED_INFO) { + auto client = find_client(evt->window); + if (!client) { + return; + } - m_log.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) - return; + auto xd = client->xembed(); + auto win = client->window(); - auto xd = client->xembed(); - auto win = client->window(); + if (evt->state == XCB_PROPERTY_NEW_VALUE) { + m_log.trace("tray: _XEMBED_INFO value has changed"); + } - if (evt->state == XCB_PROPERTY_NEW_VALUE) { - m_log.trace("tray: _XEMBED_INFO value has changed"); + xembed::query(m_connection, win, xd); + + m_log.trace("tray: _XEMBED_INFO[0]=%u _XEMBED_INFO[1]=%u", xd->version, xd->flags); + + if ((client->xembed()->flags & XEMBED_MAPPED) & XEMBED_MAPPED) { + reconfigure(); + } } - - xembed::query(m_connection, win, xd); - m_log.trace("tray: _XEMBED_INFO[0]=%u _XEMBED_INFO[1]=%u", xd->version, xd->flags); - - reconfigure(); } // }}} /** * Event callback : XCB_REPARENT_NOTIFY */ void traymanager::handle(const evt::reparent_notify& evt) { // {{{ - if (!m_activated) + if (!m_activated) { return; + } auto client = find_client(evt->window); - if (client && evt->parent != m_tray) { m_log.trace("tray: Received reparent_notify for client, remove..."); remove_client(client); @@ -842,48 +1024,54 @@ void traymanager::handle(const evt::destroy_notify& evt) { // {{{ * Event callback : XCB_MAP_NOTIFY */ void traymanager::handle(const evt::map_notify& evt) { // {{{ - if (!m_activated) + if (!m_activated) { return; + } if (evt->window == m_tray && !m_mapped) { - if (m_mapped) + if (m_mapped) { return; + } m_log.trace("tray: Received map_notify"); m_log.trace("tray: Update container mapped flag"); m_mapped = true; - reconfigure(); } else { auto client = find_client(evt->window); if (client) { m_log.trace("tray: Received map_notify"); m_log.trace("tray: Set client mapped"); client->mapped(true); - reconfigure(); } } + + if (mapped_clients() > m_settings.configured_slots) { + reconfigure(); + } } // }}} /** * Event callback : XCB_UNMAP_NOTIFY */ void traymanager::handle(const evt::unmap_notify& evt) { // {{{ - if (!m_activated) + if (!m_activated) { return; + } if (evt->window == m_tray) { m_log.trace("tray: Received unmap_notify"); - if (!m_mapped) + if (!m_mapped) { return; + } m_log.trace("tray: Update container mapped flag"); m_mapped = false; - reconfigure(); + m_settings.configured_w = 0; + m_settings.configured_x = 0; } else { auto client = find_client(evt->window); if (client) { m_log.trace("tray: Received unmap_notify"); m_log.trace("tray: Set client unmapped"); client->mapped(true); - reconfigure(); } } } // }}}