2016-11-12 06:56:39 -05:00
|
|
|
#include <xcb/xcb_icccm.h>
|
2016-11-04 13:50:33 -04:00
|
|
|
#include <xcb/xcb_image.h>
|
2016-11-20 17:04:31 -05:00
|
|
|
#include <thread>
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
#include "components/config.hpp"
|
2016-11-20 17:04:31 -05:00
|
|
|
#include "components/types.hpp"
|
2016-11-25 07:55:15 -05:00
|
|
|
#include "errors.hpp"
|
2016-12-05 14:41:00 -05:00
|
|
|
#include "events/signal.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
#include "utils/color.hpp"
|
2016-11-20 17:04:31 -05:00
|
|
|
#include "utils/factory.hpp"
|
2016-12-05 14:41:00 -05:00
|
|
|
#include "utils/math.hpp"
|
2016-11-12 06:56:39 -05:00
|
|
|
#include "utils/memory.hpp"
|
|
|
|
#include "utils/process.hpp"
|
2016-11-26 00:13:20 -05:00
|
|
|
#include "x11/atoms.hpp"
|
2016-11-20 17:04:31 -05:00
|
|
|
#include "x11/color.hpp"
|
2016-11-12 06:56:39 -05:00
|
|
|
#include "x11/connection.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
#include "x11/draw.hpp"
|
2016-11-20 17:04:31 -05:00
|
|
|
#include "x11/graphics.hpp"
|
2016-12-05 14:41:00 -05:00
|
|
|
#include "x11/tray_manager.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
#include "x11/window.hpp"
|
2016-11-24 22:10:26 -05:00
|
|
|
#include "x11/winspec.hpp"
|
2016-11-04 13:50:33 -04:00
|
|
|
#include "x11/wm.hpp"
|
2016-11-12 06:56:39 -05:00
|
|
|
#include "x11/xembed.hpp"
|
2017-01-19 14:37:01 -05:00
|
|
|
#include "x11/xresources.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-26 09:42:48 -05:00
|
|
|
// ====================================================================================================
|
|
|
|
//
|
|
|
|
// TODO: 32-bit visual
|
|
|
|
//
|
|
|
|
// _NET_SYSTEM_TRAY_VISUAL visual_id VISUALID/32
|
|
|
|
//
|
|
|
|
// The property should be set by the tray manager to indicate the preferred visual for icon windows.
|
|
|
|
//
|
|
|
|
// To avoid ambiguity about the colormap to use this visual must either be the default visual for
|
|
|
|
// the screen or it must be a TrueColor visual. If this property is set to a visual with an alpha
|
|
|
|
// channel, the tray manager must use the Composite extension to composite the icon against the
|
|
|
|
// background using PictOpOver.
|
|
|
|
//
|
|
|
|
// ====================================================================================================
|
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
using namespace wm_util;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-09 03:02:47 -05:00
|
|
|
/**
|
|
|
|
* Create instance
|
|
|
|
*/
|
2016-12-09 03:40:46 -05:00
|
|
|
tray_manager::make_type tray_manager::make() {
|
2016-12-09 03:02:47 -05:00
|
|
|
return factory_util::unique<tray_manager>(connection::make(), signal_emitter::make(), logger::make());
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
tray_manager::tray_manager(connection& conn, signal_emitter& emitter, const logger& logger)
|
2016-12-14 05:34:09 -05:00
|
|
|
: m_connection(conn), m_sig(emitter), m_log(logger) {
|
|
|
|
m_connection.attach_sink(this, SINK_PRIORITY_TRAY);
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
tray_manager::~tray_manager() {
|
|
|
|
if (m_delaythread.joinable()) {
|
|
|
|
m_delaythread.join();
|
|
|
|
}
|
2016-12-14 05:34:09 -05:00
|
|
|
m_connection.detach_sink(this, SINK_PRIORITY_TRAY);
|
2016-12-05 14:41:00 -05:00
|
|
|
deactivate();
|
2016-12-04 21:49:57 -05:00
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
void tray_manager::setup(const bar_settings& bar_opts) {
|
2017-01-19 15:27:00 -05:00
|
|
|
const config& conf = config::make();
|
2016-12-13 23:12:15 -05:00
|
|
|
auto bs = conf.section();
|
2016-12-30 17:32:05 -05:00
|
|
|
string position;
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2016-12-30 17:32:05 -05:00
|
|
|
try {
|
|
|
|
position = conf.get(bs, "tray-position");
|
|
|
|
} catch (const key_error& err) {
|
2016-12-14 11:19:32 -05:00
|
|
|
return m_log.info("Disabling tray manager (reason: missing `tray-position`)");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (position == "left") {
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.align = alignment::LEFT;
|
2016-12-14 11:19:32 -05:00
|
|
|
} else if (position == "right") {
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.align = alignment::RIGHT;
|
2016-12-14 11:19:32 -05:00
|
|
|
} else if (position == "center") {
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.align = alignment::CENTER;
|
|
|
|
} else {
|
2016-12-14 11:19:32 -05:00
|
|
|
return m_log.err("Disabling tray manager (reason: Invalid position \"" + position + "\")");
|
2016-12-04 21:49:57 -05:00
|
|
|
}
|
|
|
|
|
2016-12-30 17:32:05 -05:00
|
|
|
m_opts.detached = conf.get(bs, "tray-detached", false);
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.height = bar_opts.size.h;
|
|
|
|
m_opts.height -= bar_opts.borders.at(edge::BOTTOM).size;
|
|
|
|
m_opts.height -= bar_opts.borders.at(edge::TOP).size;
|
|
|
|
m_opts.height_fill = m_opts.height;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_opts.height % 2 != 0) {
|
|
|
|
m_opts.height--;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-30 17:32:05 -05:00
|
|
|
auto maxsize = conf.get(bs, "tray-maxsize", 16);
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_opts.height > maxsize) {
|
|
|
|
m_opts.spacing += (m_opts.height - maxsize) / 2;
|
|
|
|
m_opts.height = maxsize;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.width_max = bar_opts.size.w;
|
|
|
|
m_opts.width = m_opts.height;
|
|
|
|
m_opts.orig_y = bar_opts.pos.y + bar_opts.borders.at(edge::TOP).size;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Apply user-defined scaling
|
2016-12-30 17:32:05 -05:00
|
|
|
auto scale = conf.get(bs, "tray-scale", 1.0f);
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.width *= scale;
|
|
|
|
m_opts.height_fill *= scale;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
auto inner_area = bar_opts.inner_area(true);
|
|
|
|
|
|
|
|
switch (m_opts.align) {
|
|
|
|
case alignment::NONE:
|
|
|
|
break;
|
|
|
|
case alignment::LEFT:
|
|
|
|
m_opts.orig_x = inner_area.x;
|
|
|
|
break;
|
|
|
|
case alignment::CENTER:
|
|
|
|
m_opts.orig_x = inner_area.x + inner_area.width / 2 - m_opts.width / 2;
|
|
|
|
break;
|
|
|
|
case alignment::RIGHT:
|
|
|
|
m_opts.orig_x = inner_area.x + inner_area.width;
|
|
|
|
break;
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Set user-defined background color
|
2016-12-30 17:32:05 -05:00
|
|
|
if (!(m_opts.transparent = conf.get(bs, "tray-transparent", m_opts.transparent))) {
|
|
|
|
auto bg = conf.get(bs, "tray-background", ""s);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (bg.length() > 7) {
|
|
|
|
m_log.warn("Alpha support for the systray is limited. See the wiki for more details.");
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (!bg.empty()) {
|
|
|
|
m_opts.background = color::parse(bg, g_colorempty);
|
|
|
|
} else {
|
|
|
|
m_opts.background = bar_opts.background;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (color_util::alpha_channel(m_opts.background) == 0) {
|
|
|
|
m_opts.transparent = true;
|
|
|
|
m_opts.background = 0;
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Add user-defined padding
|
2016-12-30 17:32:05 -05:00
|
|
|
m_opts.spacing += conf.get(bs, "tray-padding", 0);
|
2016-11-25 07:55:15 -05:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Add user-defiend offset
|
2016-12-30 17:32:05 -05:00
|
|
|
auto offset_x_def = conf.get(bs, "tray-offset-x", ""s);
|
|
|
|
auto offset_y_def = conf.get(bs, "tray-offset-y", ""s);
|
2016-12-05 14:41:00 -05:00
|
|
|
|
|
|
|
auto offset_x = atoi(offset_x_def.c_str());
|
|
|
|
auto offset_y = atoi(offset_y_def.c_str());
|
|
|
|
|
|
|
|
if (offset_x != 0 && offset_x_def.find('%') != string::npos) {
|
|
|
|
if (m_opts.detached) {
|
|
|
|
offset_x = math_util::percentage_to_value<int>(offset_x, bar_opts.monitor->w);
|
|
|
|
} else {
|
|
|
|
offset_x = math_util::percentage_to_value<int>(offset_x, inner_area.width);
|
|
|
|
}
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (offset_y != 0 && offset_y_def.find('%') != string::npos) {
|
|
|
|
if (m_opts.detached) {
|
|
|
|
offset_y = math_util::percentage_to_value<int>(offset_y, bar_opts.monitor->h);
|
|
|
|
} else {
|
|
|
|
offset_y = math_util::percentage_to_value<int>(offset_y, inner_area.height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_opts.orig_x += offset_x;
|
|
|
|
m_opts.orig_y += offset_y;
|
|
|
|
|
|
|
|
// Put the tray next to the bar in the window stack
|
|
|
|
m_opts.sibling = bar_opts.window;
|
|
|
|
|
|
|
|
// Activate the tray manager
|
|
|
|
query_atom();
|
|
|
|
activate();
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
/**
|
|
|
|
* Get the settings container
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
const tray_settings tray_manager::settings() const {
|
2016-11-12 06:56:39 -05:00
|
|
|
return m_opts;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-12 06:56:39 -05:00
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
/**
|
|
|
|
* Activate systray management
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::activate() {
|
2016-11-02 15:22:45 -04:00
|
|
|
if (m_activated) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-24 22:10:26 -05:00
|
|
|
m_log.info("Activating tray manager");
|
2016-11-02 15:22:45 -04:00
|
|
|
m_activated = true;
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.running = true;
|
|
|
|
|
|
|
|
m_sig.attach(this);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
try {
|
|
|
|
create_window();
|
|
|
|
create_bg();
|
|
|
|
restack_window();
|
2016-12-05 14:41:00 -05:00
|
|
|
set_wm_hints();
|
|
|
|
set_tray_colors();
|
2016-11-12 06:56:39 -05:00
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err(err.what());
|
2016-11-24 22:10:26 -05:00
|
|
|
m_log.err("Cannot activate tray manager... failed to setup window");
|
2016-11-12 06:56:39 -05:00
|
|
|
m_activated = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Make sure we receive notificatins when the root pixmap changes
|
|
|
|
m_connection.ensure_event_mask(m_connection.root(), XCB_EVENT_MASK_PROPERTY_CHANGE);
|
|
|
|
m_connection.flush();
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
// Attempt to get control of the systray selection then
|
|
|
|
// notify clients waiting for a manager.
|
|
|
|
acquire_selection();
|
|
|
|
|
2016-12-21 17:22:02 -05:00
|
|
|
if (!m_acquired_selection) {
|
|
|
|
deactivate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-25 07:55:15 -05:00
|
|
|
// Send delayed notification
|
2016-12-14 05:34:09 -05:00
|
|
|
if (!m_firstactivation) {
|
|
|
|
notify_clients();
|
2016-12-21 17:22:02 -05:00
|
|
|
} else {
|
|
|
|
notify_clients_delayed();
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-14 05:34:09 -05:00
|
|
|
|
|
|
|
m_firstactivation = false;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Deactivate systray management
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::deactivate(bool clear_selection) {
|
2016-11-02 15:22:45 -04:00
|
|
|
if (!m_activated) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-24 22:10:26 -05:00
|
|
|
m_log.info("Deactivating tray manager");
|
2016-11-02 15:22:45 -04:00
|
|
|
m_activated = false;
|
2016-12-05 14:41:00 -05:00
|
|
|
m_opts.running = false;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
m_sig.detach(this);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-25 07:55:15 -05:00
|
|
|
if (!m_connection.connection_has_error() && clear_selection && m_acquired_selection) {
|
2016-11-24 22:10:26 -05:00
|
|
|
m_log.trace("tray: Unset selection owner");
|
|
|
|
m_connection.set_selection_owner(XCB_NONE, m_atom, XCB_CURRENT_TIME);
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_log.trace("tray: Unembed clients");
|
|
|
|
m_clients.clear();
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
if (m_tray) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.trace("tray: Destroy window");
|
|
|
|
m_connection.destroy_window(m_tray);
|
|
|
|
}
|
2016-11-12 06:56:39 -05:00
|
|
|
if (m_pixmap) {
|
|
|
|
m_connection.free_pixmap(m_pixmap);
|
|
|
|
}
|
|
|
|
if (m_gc) {
|
|
|
|
m_connection.free_gc(m_pixmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_tray = 0;
|
|
|
|
m_pixmap = 0;
|
|
|
|
m_gc = 0;
|
|
|
|
m_rootpixmap.pixmap = 0;
|
2016-11-14 18:55:40 -05:00
|
|
|
m_prevwidth = 0;
|
|
|
|
m_prevheight = 0;
|
2016-11-24 22:10:26 -05:00
|
|
|
m_opts.configured_x = 0;
|
|
|
|
m_opts.configured_y = 0;
|
|
|
|
m_opts.configured_w = 0;
|
|
|
|
m_opts.configured_h = 0;
|
|
|
|
m_opts.configured_slots = 0;
|
2016-11-25 07:55:15 -05:00
|
|
|
m_acquired_selection = false;
|
2016-12-03 14:26:29 -05:00
|
|
|
m_mapped = false;
|
2016-11-12 06:56:39 -05:00
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
m_connection.flush();
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2017-01-09 08:27:55 -05:00
|
|
|
m_sig.emit(notify_forcechange{});
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reconfigure tray
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::reconfigure() {
|
|
|
|
if (!m_tray || !m_mtx.try_lock()) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-22 23:18:58 -05:00
|
|
|
std::unique_lock<mutex> guard(m_mtx, std::adopt_lock);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-04 13:50:33 -04:00
|
|
|
try {
|
|
|
|
reconfigure_clients();
|
2016-11-12 06:56:39 -05:00
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to reconfigure tray clients (%s)", err.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2016-11-04 13:50:33 -04:00
|
|
|
reconfigure_window();
|
2016-11-12 06:56:39 -05:00
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to reconfigure tray window (%s)", err.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2016-11-04 13:50:33 -04:00
|
|
|
reconfigure_bg();
|
2016-11-12 06:56:39 -05:00
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to reconfigure tray background (%s)", err.what());
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
m_opts.configured_slots = mapped_clients();
|
2016-11-04 13:50:33 -04:00
|
|
|
|
|
|
|
guard.unlock();
|
|
|
|
|
|
|
|
refresh_window();
|
2016-12-03 14:26:29 -05:00
|
|
|
|
|
|
|
m_connection.flush();
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2017-01-09 08:27:55 -05:00
|
|
|
m_sig.emit(notify_forcechange{});
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reconfigure container window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::reconfigure_window() {
|
|
|
|
m_log.trace("tray: Reconfigure window (mapped=%i, clients=%i)", static_cast<bool>(m_mapped), m_clients.size());
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-03 17:01:21 -05:00
|
|
|
if (!m_tray) {
|
2016-11-04 13:50:33 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
auto clients = mapped_clients();
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (!clients && m_mapped) {
|
2016-11-04 13:50:33 -04:00
|
|
|
m_log.trace("tray: Reconfigure window / unmap");
|
|
|
|
m_connection.unmap_window_checked(m_tray);
|
2016-12-03 17:01:21 -05:00
|
|
|
} else if (clients && !m_mapped && !m_hidden) {
|
2016-11-04 13:50:33 -04:00
|
|
|
m_log.trace("tray: Reconfigure window / map");
|
|
|
|
m_connection.map_window_checked(m_tray);
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-04 13:50:33 -04:00
|
|
|
auto width = calculate_w();
|
|
|
|
auto x = calculate_x(width);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
if (m_opts.configured_w == width && m_opts.configured_x == x) {
|
2016-11-04 13:50:33 -04:00
|
|
|
m_log.trace("tray: Reconfigure window / ignoring unchanged values w=%d x=%d", width, x);
|
|
|
|
return;
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (width > 0) {
|
|
|
|
m_log.trace("tray: New window values, width=%d, x=%d", width, x);
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
uint32_t mask = 0;
|
|
|
|
uint32_t values[7];
|
2016-12-15 03:29:14 -05:00
|
|
|
xcb_params_configure_window_t params{};
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
XCB_AUX_ADD_PARAM(&mask, ¶ms, width, width);
|
|
|
|
XCB_AUX_ADD_PARAM(&mask, ¶ms, x, x);
|
2016-12-31 01:39:50 -05:00
|
|
|
connection::pack_values(mask, ¶ms, values);
|
2016-12-05 14:41:00 -05:00
|
|
|
m_connection.configure_window_checked(m_tray, mask, values);
|
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
m_opts.configured_w = width;
|
|
|
|
m_opts.configured_x = x;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reconfigure clients
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::reconfigure_clients() {
|
2016-11-04 13:50:33 -04:00
|
|
|
m_log.trace("tray: Reconfigure clients");
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
uint32_t x = m_opts.spacing;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
for (auto it = m_clients.rbegin(); it != m_clients.rend(); it++) {
|
|
|
|
auto client = *it;
|
|
|
|
|
|
|
|
try {
|
|
|
|
client->ensure_state();
|
|
|
|
client->reconfigure(x, calculate_client_y());
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
x += m_opts.width + m_opts.spacing;
|
2016-11-02 15:22:45 -04:00
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
remove_client(client, false);
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-04 13:50:33 -04:00
|
|
|
/**
|
|
|
|
* Reconfigure root pixmap
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::reconfigure_bg(bool realloc) {
|
2016-11-12 06:56:39 -05:00
|
|
|
if (!m_opts.transparent || m_clients.empty() || !m_mapped) {
|
2016-11-04 13:50:33 -04:00
|
|
|
return;
|
2016-12-03 14:26:29 -05:00
|
|
|
} else if (!m_rootpixmap.pixmap) {
|
|
|
|
realloc = true;
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
auto w = calculate_w();
|
|
|
|
auto h = calculate_h();
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
if ((!w || (w == m_prevwidth && h == m_prevheight)) && !realloc) {
|
|
|
|
return;
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
m_log.trace("tray: Reconfigure bg (realloc=%i)", realloc);
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-03 14:26:29 -05:00
|
|
|
if (realloc && !get_root_pixmap(m_connection, &m_rootpixmap)) {
|
2016-11-12 06:56:39 -05:00
|
|
|
return m_log.err("Failed to get root pixmap for tray background (realloc=%i)", realloc);
|
2016-12-05 14:41:00 -05:00
|
|
|
} else if (realloc) {
|
2016-12-04 21:49:57 -05:00
|
|
|
// clang-format off
|
|
|
|
m_log.info("Tray root pixmap (rootpmap=%s, geom=%dx%d+%d+%d, tray=%s, pmap=%s, gc=%s)",
|
|
|
|
m_connection.id(m_rootpixmap.pixmap),
|
|
|
|
m_rootpixmap.width,
|
|
|
|
m_rootpixmap.height,
|
|
|
|
m_rootpixmap.x,
|
|
|
|
m_rootpixmap.y,
|
|
|
|
m_connection.id(m_tray),
|
|
|
|
m_connection.id(m_pixmap),
|
|
|
|
m_connection.id(m_gc));
|
|
|
|
// clang-format on
|
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
m_prevwidth = w;
|
|
|
|
m_prevheight = h;
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
auto x = calculate_x(w);
|
2016-11-04 13:50:33 -04:00
|
|
|
auto y = calculate_y();
|
2016-12-05 14:41:00 -05:00
|
|
|
auto px = math_util::max(0, m_rootpixmap.x + x);
|
|
|
|
auto py = math_util::max(0, m_rootpixmap.y + y);
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-04 21:49:57 -05:00
|
|
|
// Make sure we don't try to copy void content
|
|
|
|
if (px + w > m_rootpixmap.width) {
|
|
|
|
w -= px + w - m_rootpixmap.width;
|
|
|
|
}
|
|
|
|
if (py + h > m_rootpixmap.height) {
|
|
|
|
h -= py + h - m_rootpixmap.height;
|
|
|
|
}
|
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
if (realloc) {
|
|
|
|
vector<uint8_t> image_data;
|
|
|
|
uint8_t image_depth;
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
try {
|
|
|
|
auto image_reply =
|
|
|
|
m_connection.get_image(XCB_IMAGE_FORMAT_Z_PIXMAP, m_rootpixmap.pixmap, px, py, w, h, XCB_COPY_PLANE);
|
|
|
|
image_depth = image_reply->depth;
|
|
|
|
std::back_insert_iterator<decltype(image_data)> back_it(image_data);
|
|
|
|
std::copy(image_reply.data().begin(), image_reply.data().end(), back_it);
|
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to get slice of root pixmap (%s)", err.what());
|
|
|
|
return;
|
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
try {
|
|
|
|
m_connection.put_image_checked(
|
|
|
|
XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmap, m_gc, w, h, 0, 0, 0, image_depth, image_data.size(), image_data.data());
|
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to store slice of root pixmap (%s)", err.what());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
m_connection.copy_area_checked(m_rootpixmap.pixmap, m_pixmap, m_gc, px, py, 0, 0, w, h);
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
|
|
|
/**
|
2016-11-12 06:56:39 -05:00
|
|
|
* Refresh the bar window by clearing it along with each client window
|
2016-11-04 13:50:33 -04:00
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::refresh_window() {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (!m_activated || !m_mapped || !m_mtx.try_lock()) {
|
2016-11-04 13:50:33 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-22 23:18:58 -05:00
|
|
|
std::lock_guard<mutex> lock(m_mtx, std::adopt_lock);
|
2016-11-04 13:50:33 -04:00
|
|
|
|
|
|
|
m_log.trace("tray: Refreshing window");
|
|
|
|
|
|
|
|
auto width = calculate_w();
|
|
|
|
auto height = calculate_h();
|
2016-11-12 06:56:39 -05:00
|
|
|
|
|
|
|
if (m_opts.transparent && !m_rootpixmap.pixmap) {
|
|
|
|
draw_util::fill(m_connection, m_pixmap, m_gc, 0, 0, width, height);
|
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-12-04 21:49:57 -05:00
|
|
|
m_connection.clear_area(0, m_tray, 0, 0, width, height);
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
for (auto&& client : m_clients) {
|
2016-12-04 21:49:57 -05:00
|
|
|
client->clear_window();
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_connection.flush();
|
2016-12-05 14:41:00 -05:00
|
|
|
|
|
|
|
if (!mapped_clients()) {
|
|
|
|
m_opts.configured_w = 0;
|
|
|
|
} else {
|
|
|
|
m_opts.configured_w = width;
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redraw window
|
|
|
|
*/
|
2016-12-04 21:49:57 -05:00
|
|
|
void tray_manager::redraw_window(bool realloc_bg) {
|
2016-12-14 05:34:09 -05:00
|
|
|
chrono::system_clock::time_point now{chrono::system_clock::now()};
|
|
|
|
|
|
|
|
if (!realloc_bg && now - 24ms < m_drawtime) {
|
|
|
|
return m_log.trace("tray: Ignoring redraw (throttled)");
|
|
|
|
}
|
|
|
|
|
|
|
|
m_drawtime = chrono::time_point_cast<chrono::milliseconds>(now);
|
|
|
|
m_log.info("Redraw tray container (id=%s) %lu", m_connection.id(m_tray),
|
|
|
|
chrono::duration_cast<chrono::microseconds>(chrono::system_clock::now().time_since_epoch()));
|
2016-12-04 21:49:57 -05:00
|
|
|
reconfigure_bg(realloc_bg);
|
2016-12-03 14:26:29 -05:00
|
|
|
refresh_window();
|
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
/**
|
|
|
|
* Find the systray selection atom
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::query_atom() {
|
2016-11-02 15:22:45 -04:00
|
|
|
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();
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create tray window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::create_window() {
|
2016-11-24 22:10:26 -05:00
|
|
|
m_log.trace("tray: Create tray window");
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-25 02:42:31 -05:00
|
|
|
// clang-format off
|
2016-11-24 22:10:26 -05:00
|
|
|
auto win = winspec(m_connection, m_tray)
|
|
|
|
<< cw_size(calculate_w(), calculate_h())
|
|
|
|
<< cw_pos(calculate_x(calculate_w()), calculate_y())
|
|
|
|
<< cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT)
|
|
|
|
<< cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED)
|
2016-12-04 21:49:57 -05:00
|
|
|
<< cw_params_event_mask(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
|
|
|
|
|XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
|
|
|
|XCB_EVENT_MASK_EXPOSURE)
|
2016-11-25 02:42:31 -05:00
|
|
|
<< cw_params_override_redirect(true);
|
|
|
|
// clang-format on
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
if (!m_opts.transparent) {
|
2016-11-24 22:10:26 -05:00
|
|
|
win << cw_params_back_pixel(m_opts.background);
|
|
|
|
win << cw_params_border_pixel(m_opts.background);
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
m_tray = win << cw_flush(true);
|
2016-11-24 22:10:26 -05:00
|
|
|
m_log.info("Tray window: %s", m_connection.id(m_tray));
|
2016-11-24 17:57:25 -05:00
|
|
|
|
2016-12-31 01:39:50 -05:00
|
|
|
const uint32_t shadow{0};
|
|
|
|
m_connection.change_property(XCB_PROP_MODE_REPLACE, m_tray, _COMPTON_SHADOW, XCB_ATOM_CARDINAL, 32, 1, &shadow);
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-12 06:56:39 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create tray window background components
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::create_bg(bool realloc) {
|
2016-11-12 06:56:39 -05:00
|
|
|
if (!m_opts.transparent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!realloc && m_pixmap && m_gc && m_rootpixmap.pixmap) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (realloc && m_pixmap) {
|
|
|
|
m_connection.free_pixmap(m_pixmap);
|
|
|
|
m_pixmap = 0;
|
|
|
|
}
|
|
|
|
if (realloc && m_gc) {
|
|
|
|
m_connection.free_gc(m_gc);
|
|
|
|
m_gc = 0;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
auto w = m_opts.width_max;
|
|
|
|
auto h = calculate_h();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
if (!m_pixmap && !graphics_util::create_pixmap(m_connection, m_tray, w, h, &m_pixmap)) {
|
|
|
|
return m_log.err("Failed to create pixmap for tray background");
|
|
|
|
} else if (!m_gc && !graphics_util::create_gc(m_connection, m_pixmap, &m_gc)) {
|
|
|
|
return m_log.err("Failed to create gcontext for tray background");
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2016-11-26 00:13:20 -05:00
|
|
|
m_connection.change_window_attributes_checked(m_tray, XCB_CW_BACK_PIXMAP, &m_pixmap);
|
2016-11-12 06:56:39 -05:00
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to set tray window back pixmap (%s)", err.what());
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-12 06:56:39 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Put tray window above the defined sibling in the window stack
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::restack_window() {
|
2016-12-15 15:00:17 -05:00
|
|
|
if (m_opts.sibling == XCB_NONE) {
|
2016-11-12 06:56:39 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
m_log.trace("tray: Restacking tray window");
|
|
|
|
|
|
|
|
uint32_t mask = 0;
|
|
|
|
uint32_t values[7];
|
2016-12-15 03:29:14 -05:00
|
|
|
xcb_params_configure_window_t params{};
|
2016-11-12 06:56:39 -05:00
|
|
|
|
|
|
|
XCB_AUX_ADD_PARAM(&mask, ¶ms, sibling, m_opts.sibling);
|
|
|
|
XCB_AUX_ADD_PARAM(&mask, ¶ms, stack_mode, XCB_STACK_MODE_ABOVE);
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2016-12-31 01:39:50 -05:00
|
|
|
connection::pack_values(mask, ¶ms, values);
|
2016-11-12 06:56:39 -05:00
|
|
|
m_connection.configure_window_checked(m_tray, mask, values);
|
|
|
|
} catch (const exception& err) {
|
|
|
|
auto id = m_connection.id(m_opts.sibling);
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.trace("tray: Failed to put tray above %s in the stack (%s)", id, err.what());
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set window WM hints
|
|
|
|
*/
|
2016-12-05 14:41:00 -05:00
|
|
|
void tray_manager::set_wm_hints() {
|
|
|
|
const uint32_t visual{m_connection.screen()->root_visual};
|
|
|
|
const uint32_t orientation{_NET_SYSTEM_TRAY_ORIENTATION_HORZ};
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.trace("tray: Set window WM_NAME / WM_CLASS", m_connection.id(m_tray));
|
2016-11-19 00:22:44 -05:00
|
|
|
xcb_icccm_set_wm_name(m_connection, m_tray, XCB_ATOM_STRING, 8, 19, TRAY_WM_NAME);
|
|
|
|
xcb_icccm_set_wm_class(m_connection, m_tray, 12, TRAY_WM_CLASS);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
m_log.trace("tray: Set window WM_PROTOCOLS");
|
2016-12-05 14:41:00 -05:00
|
|
|
set_wm_protocols(m_connection, m_tray, {WM_DELETE_WINDOW, WM_TAKE_FOCUS});
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
m_log.trace("tray: Set window _NET_WM_WINDOW_TYPE");
|
2016-12-05 14:41:00 -05:00
|
|
|
set_wm_window_type(m_connection, m_tray, {_NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL});
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
m_log.trace("tray: Set window _NET_WM_STATE");
|
2016-12-05 14:41:00 -05:00
|
|
|
set_wm_state(m_connection, m_tray, {_NET_WM_STATE_SKIP_TASKBAR});
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-04 13:50:33 -04:00
|
|
|
m_log.trace("tray: Set window _NET_WM_PID");
|
2016-12-05 14:41:00 -05:00
|
|
|
set_wm_pid(m_connection, m_tray, getpid());
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_VISUAL");
|
2016-12-05 14:41:00 -05:00
|
|
|
xcb_change_property(
|
|
|
|
m_connection, XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_VISUAL, XCB_ATOM_VISUALID, 32, 1, &visual);
|
|
|
|
|
|
|
|
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_ORIENTATION");
|
|
|
|
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_ORIENTATION,
|
|
|
|
_NET_SYSTEM_TRAY_ORIENTATION, 32, 1, &orientation);
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set color atom used by clients when determing icon theme
|
|
|
|
*/
|
2016-12-05 14:41:00 -05:00
|
|
|
void tray_manager::set_tray_colors() {
|
2016-11-12 06:56:39 -05:00
|
|
|
m_log.trace("tray: Set _NET_SYSTEM_TRAY_COLORS to %x", m_opts.background);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
auto r = color_util::red_channel(m_opts.background);
|
|
|
|
auto g = color_util::green_channel(m_opts.background);
|
|
|
|
auto b = color_util::blue_channel(m_opts.background);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
const uint32_t colors[12] = {
|
|
|
|
r, g, b, // normal
|
|
|
|
r, g, b, // error
|
|
|
|
r, g, b, // warning
|
|
|
|
r, g, b, // success
|
|
|
|
};
|
|
|
|
|
|
|
|
m_connection.change_property(
|
|
|
|
XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_COLORS, XCB_ATOM_CARDINAL, 32, 12, colors);
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Acquire the systray selection
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::acquire_selection() {
|
2016-12-14 05:34:09 -05:00
|
|
|
m_othermanager = XCB_NONE;
|
|
|
|
xcb_window_t owner;
|
|
|
|
|
|
|
|
try {
|
|
|
|
owner = m_connection.get_selection_owner(m_atom).owner<xcb_window_t>();
|
|
|
|
} catch (const exception& err) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
if (owner == m_tray) {
|
2016-12-14 05:34:09 -05:00
|
|
|
m_log.trace("tray: Already managing the systray selection");
|
|
|
|
m_acquired_selection = true;
|
2016-12-21 17:22:02 -05:00
|
|
|
} else if ((m_othermanager = owner) != XCB_NONE) {
|
|
|
|
m_log.warn("Systray selection already managed (window=%s)", m_connection.id(owner));
|
|
|
|
track_selection_owner(m_othermanager);
|
|
|
|
} else {
|
|
|
|
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_acquired_selection = true;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Notify pending clients about the new systray MANAGER
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::notify_clients() {
|
2016-11-25 07:55:15 -05:00
|
|
|
if (m_activated) {
|
|
|
|
m_log.info("Notifying pending tray 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;
|
|
|
|
message->data.data32[2] = m_tray;
|
|
|
|
m_connection.send_client_message(message, m_connection.root());
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-25 07:55:15 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Send delayed notification to pending clients
|
|
|
|
*/
|
2016-12-14 05:34:09 -05:00
|
|
|
void tray_manager::notify_clients_delayed() {
|
|
|
|
if (m_delaythread.joinable()) {
|
2016-11-25 07:55:15 -05:00
|
|
|
m_delaythread.join();
|
|
|
|
}
|
2016-12-14 05:34:09 -05:00
|
|
|
m_delaythread = thread([this]() {
|
|
|
|
this_thread::sleep_for(1s);
|
2016-11-25 07:55:15 -05:00
|
|
|
notify_clients();
|
2016-12-14 05:34:09 -05:00
|
|
|
});
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Track changes to the given selection owner
|
2016-11-12 06:56:39 -05:00
|
|
|
* If it gets destroyed or goes away we can reactivate the tray_manager
|
2016-11-02 15:22:45 -04:00
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::track_selection_owner(xcb_window_t owner) {
|
2016-12-14 05:34:09 -05:00
|
|
|
if (owner != XCB_NONE) {
|
2016-11-26 00:13:20 -05:00
|
|
|
m_log.trace("tray: Listen for events on the new selection window");
|
|
|
|
const uint32_t mask{XCB_CW_EVENT_MASK};
|
|
|
|
const uint32_t values[]{XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
|
|
|
m_connection.change_window_attributes(owner, mask, values);
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Process client docking request
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::process_docking_request(xcb_window_t win) {
|
2016-11-04 13:50:33 -04:00
|
|
|
m_log.info("Processing docking request from %s", m_connection.id(win));
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
m_clients.emplace_back(factory_util::shared<tray_client>(m_connection, win, m_opts.width, m_opts.height));
|
|
|
|
auto& client = m_clients.back();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
m_log.trace("tray: Get client _XEMBED_INFO");
|
|
|
|
xembed::query(m_connection, win, client->xembed());
|
|
|
|
} catch (const application_error& err) {
|
|
|
|
m_log.err(err.what());
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
m_log.err("Failed to query for _XEMBED_INFO, removing client... (%s)", err.what());
|
2016-12-05 14:41:00 -05:00
|
|
|
remove_client(win, false);
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2016-12-05 14:41:00 -05:00
|
|
|
const uint32_t mask{XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK};
|
|
|
|
const uint32_t values[]{
|
|
|
|
XCB_BACK_PIXMAP_PARENT_RELATIVE, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.trace("tray: Update client window");
|
2016-12-05 14:41:00 -05:00
|
|
|
m_connection.change_window_attributes_checked(client->window(), mask, values);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
m_log.trace("tray: Configure client size");
|
|
|
|
client->reconfigure(0, 0);
|
|
|
|
|
|
|
|
m_log.trace("tray: Add client window to the save set");
|
|
|
|
m_connection.change_save_set_checked(XCB_SET_MODE_INSERT, client->window());
|
|
|
|
|
|
|
|
m_log.trace("tray: Reparent client");
|
|
|
|
m_connection.reparent_window_checked(
|
|
|
|
client->window(), m_tray, calculate_client_x(client->window()), calculate_client_y());
|
|
|
|
|
|
|
|
m_log.trace("tray: Send embbeded notification to client");
|
|
|
|
xembed::notify_embedded(m_connection, client->window(), m_tray, client->xembed()->version);
|
|
|
|
|
|
|
|
if (client->xembed()->flags & XEMBED_MAPPED) {
|
|
|
|
m_log.trace("tray: Map client");
|
|
|
|
m_connection.map_window_checked(client->window());
|
|
|
|
}
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
m_log.err("Failed to setup tray client, removing... (%s)", err.what());
|
2016-12-05 14:41:00 -05:00
|
|
|
remove_client(win, false);
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate x position of tray window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
int16_t tray_manager::calculate_x(uint16_t width) const {
|
2016-11-12 06:56:39 -05:00
|
|
|
auto x = m_opts.orig_x;
|
2016-11-25 07:55:15 -05:00
|
|
|
if (m_opts.align == alignment::RIGHT) {
|
2016-11-12 06:56:39 -05:00
|
|
|
x -= ((m_opts.width + m_opts.spacing) * m_clients.size() + m_opts.spacing);
|
2016-11-25 07:55:15 -05:00
|
|
|
} else if (m_opts.align == alignment::CENTER) {
|
2016-11-12 06:56:39 -05:00
|
|
|
x -= (width / 2) - (m_opts.width / 2);
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
return x;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate y position of tray window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
int16_t tray_manager::calculate_y() const {
|
2016-11-12 06:56:39 -05:00
|
|
|
return m_opts.orig_y;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate width of tray window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
uint16_t tray_manager::calculate_w() const {
|
2016-11-12 06:56:39 -05:00
|
|
|
uint16_t width = m_opts.spacing;
|
2016-12-05 14:41:00 -05:00
|
|
|
size_t count{0};
|
2016-11-02 15:22:45 -04:00
|
|
|
for (auto&& client : m_clients) {
|
|
|
|
if (client->mapped()) {
|
2016-12-05 14:41:00 -05:00
|
|
|
count++;
|
2016-11-12 06:56:39 -05:00
|
|
|
width += m_opts.spacing + m_opts.width;
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
}
|
2016-12-05 14:41:00 -05:00
|
|
|
return count ? width : 0;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate height of tray window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
uint16_t tray_manager::calculate_h() const {
|
2016-11-12 06:56:39 -05:00
|
|
|
return m_opts.height_fill;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate x position of client window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
int16_t tray_manager::calculate_client_x(const xcb_window_t& win) {
|
2016-11-25 07:55:15 -05:00
|
|
|
for (size_t i = 0; i < m_clients.size(); i++) {
|
|
|
|
if (m_clients[i]->match(win)) {
|
2016-11-12 06:56:39 -05:00
|
|
|
return m_opts.spacing + m_opts.width * i;
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
|
|
|
}
|
2016-11-12 06:56:39 -05:00
|
|
|
return m_opts.spacing;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate y position of client window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
int16_t tray_manager::calculate_client_y() {
|
2016-11-12 06:56:39 -05:00
|
|
|
return (m_opts.height_fill - m_opts.height) / 2;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
/**
|
|
|
|
* Check if the given window is embedded
|
|
|
|
*/
|
|
|
|
bool tray_manager::is_embedded(const xcb_window_t& win) const {
|
|
|
|
return m_clients.end() != std::find_if(m_clients.begin(), m_clients.end(),
|
|
|
|
[win](shared_ptr<tray_client> client) { return client->match(win); });
|
|
|
|
}
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
/**
|
|
|
|
* Find tray client by window
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
shared_ptr<tray_client> tray_manager::find_client(const xcb_window_t& win) const {
|
2016-11-25 07:55:15 -05:00
|
|
|
for (auto&& client : m_clients) {
|
2016-11-02 15:22:45 -04:00
|
|
|
if (client->match(win)) {
|
2016-12-19 23:05:43 -05:00
|
|
|
return shared_ptr<tray_client>{client.get(), factory_util::null_deleter};
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-05 14:41:00 -05:00
|
|
|
return nullptr;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
2016-12-05 14:41:00 -05:00
|
|
|
* Remove tray client
|
2016-11-02 15:22:45 -04:00
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::remove_client(shared_ptr<tray_client>& client, bool reconfigure) {
|
2016-12-05 14:41:00 -05:00
|
|
|
remove_client(client->window(), reconfigure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove tray client by window
|
|
|
|
*/
|
|
|
|
void tray_manager::remove_client(xcb_window_t win, bool reconfigure) {
|
|
|
|
m_clients.erase(std::remove_if(
|
|
|
|
m_clients.begin(), m_clients.end(), [win](shared_ptr<tray_client> client) { return client->match(win); }));
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
if (reconfigure) {
|
2016-11-12 06:56:39 -05:00
|
|
|
tray_manager::reconfigure();
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get number of mapped clients
|
|
|
|
*/
|
2016-12-05 14:41:00 -05:00
|
|
|
size_t tray_manager::mapped_clients() const {
|
|
|
|
size_t mapped_clients = 0;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
for (auto&& client : m_clients) {
|
|
|
|
if (client->mapped()) {
|
|
|
|
mapped_clients++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mapped_clients;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_EXPOSE
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::expose& evt) {
|
|
|
|
if (m_activated && !m_clients.empty() && evt->count == 0) {
|
|
|
|
redraw_window();
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_VISIBILITY_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::visibility_notify& evt) {
|
2016-11-04 13:50:33 -04:00
|
|
|
if (m_activated && !m_clients.empty()) {
|
|
|
|
m_log.trace("tray: Received visibility_notify for %s", m_connection.id(evt->window));
|
|
|
|
reconfigure_window();
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_CLIENT_MESSAGE
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::client_message& evt) {
|
2016-11-02 15:22:45 -04:00
|
|
|
if (!m_activated) {
|
|
|
|
return;
|
2016-12-05 14:41:00 -05:00
|
|
|
} else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW && evt->window == m_tray) {
|
|
|
|
m_log.warn("Received WM_DELETE");
|
|
|
|
m_tray = 0;
|
|
|
|
deactivate();
|
|
|
|
} else if (evt->type == _NET_SYSTEM_TRAY_OPCODE && evt->format == 32) {
|
2016-11-26 00:13:20 -05:00
|
|
|
m_log.trace("tray: Received client_message");
|
|
|
|
|
|
|
|
if (SYSTEM_TRAY_REQUEST_DOCK == evt->data.data32[1]) {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (!is_embedded(evt->data.data32[2])) {
|
|
|
|
process_docking_request(evt->data.data32[2]);
|
|
|
|
} else {
|
|
|
|
auto win = evt->data.data32[2];
|
|
|
|
m_log.warn("Tray client %s already embedded, ignoring request...", m_connection.id(win));
|
|
|
|
}
|
2016-11-26 00:13:20 -05:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_CONFIGURE_REQUEST
|
|
|
|
*
|
|
|
|
* Called when a tray client thinks he's part of the free world and
|
|
|
|
* wants to reconfigure its window. This is of course nothing we appreciate
|
|
|
|
* so we return an answer that'll put him in place.
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::configure_request& evt) {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_activated && is_embedded(evt->window)) {
|
|
|
|
try {
|
|
|
|
m_log.trace("tray: Client configure request %s", m_connection.id(evt->window));
|
|
|
|
find_client(evt->window)->configure_notify(calculate_client_x(evt->window), calculate_client_y());
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
m_log.err("Failed to reconfigure tray client, removing... (%s)", err.what());
|
|
|
|
remove_client(evt->window);
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @see tray_manager::handle(const evt::configure_request&);
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::resize_request& evt) {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_activated && is_embedded(evt->window)) {
|
|
|
|
try {
|
|
|
|
m_log.trace("tray: Received resize_request for client %s", m_connection.id(evt->window));
|
|
|
|
find_client(evt->window)->configure_notify(calculate_client_x(evt->window), calculate_client_y());
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
m_log.err("Failed to reconfigure tray client, removing... (%s)", err.what());
|
|
|
|
remove_client(evt->window);
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_SELECTION_CLEAR
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::selection_clear& evt) {
|
2016-11-04 13:50:33 -04:00
|
|
|
if (!m_activated) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
2016-11-04 13:50:33 -04:00
|
|
|
} else if (evt->selection != m_atom) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
2016-11-04 13:50:33 -04:00
|
|
|
} else if (evt->owner != m_tray) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
m_log.warn("Lost systray selection, deactivating...");
|
2016-12-14 05:34:09 -05:00
|
|
|
m_othermanager = m_connection.get_selection_owner(m_atom).owner<xcb_window_t>();
|
2016-11-02 15:22:45 -04:00
|
|
|
track_selection_owner(m_othermanager);
|
2016-11-12 06:56:39 -05:00
|
|
|
} catch (const exception& err) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.err("Failed to get systray selection owner");
|
2016-12-14 05:34:09 -05:00
|
|
|
m_othermanager = XCB_NONE;
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-11-24 22:10:26 -05:00
|
|
|
deactivate(false);
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_PROPERTY_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::property_notify& evt) {
|
2016-11-04 13:50:33 -04:00
|
|
|
if (!m_activated) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
2016-11-04 13:50:33 -04:00
|
|
|
} else if (evt->atom == _XROOTMAP_ID) {
|
2016-12-04 21:49:57 -05:00
|
|
|
redraw_window(true);
|
2016-11-04 13:50:33 -04:00
|
|
|
} else if (evt->atom == _XSETROOT_ID) {
|
2016-12-04 21:49:57 -05:00
|
|
|
redraw_window(true);
|
2016-11-04 13:50:33 -04:00
|
|
|
} else if (evt->atom == ESETROOT_PMAP_ID) {
|
2016-12-04 21:49:57 -05:00
|
|
|
redraw_window(true);
|
2016-12-05 14:41:00 -05:00
|
|
|
} else if (evt->atom != _XEMBED_INFO) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-26 00:13:20 -05:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
auto client = find_client(evt->window);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (!client) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
m_log.trace("tray: _XEMBED_INFO: %s", m_connection.id(evt->window));
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
auto xd = client->xembed();
|
|
|
|
auto win = client->window();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
if (evt->state == XCB_PROPERTY_NEW_VALUE) {
|
|
|
|
m_log.trace("tray: _XEMBED_INFO value has changed");
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
xembed::query(m_connection, win, xd);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
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();
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_REPARENT_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::reparent_notify& evt) {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_activated && is_embedded(evt->window) && evt->parent != m_tray) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.trace("tray: Received reparent_notify for client, remove...");
|
2016-12-05 14:41:00 -05:00
|
|
|
remove_client(evt->window);
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_DESTROY_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::destroy_notify& evt) {
|
2016-11-24 22:10:26 -05:00
|
|
|
if (m_activated && evt->window == m_tray) {
|
|
|
|
deactivate();
|
2016-12-14 05:34:09 -05:00
|
|
|
} else if (!m_activated && evt->window == m_othermanager) {
|
2016-12-21 17:22:02 -05:00
|
|
|
m_log.info("Systray selection unmanaged... re-activating");
|
2016-12-14 05:34:09 -05:00
|
|
|
activate();
|
2016-12-05 14:41:00 -05:00
|
|
|
} else if (m_activated && is_embedded(evt->window)) {
|
|
|
|
m_log.trace("tray: Received destroy_notify for client, remove...");
|
|
|
|
remove_client(evt->window);
|
|
|
|
redraw_window();
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_MAP_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::map_notify& evt) {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_activated && evt->window == m_tray) {
|
2016-12-03 14:26:29 -05:00
|
|
|
m_log.trace("tray: Received map_notify");
|
|
|
|
m_log.trace("tray: Update container mapped flag");
|
|
|
|
m_mapped = true;
|
2016-12-05 14:41:00 -05:00
|
|
|
redraw_window();
|
|
|
|
} else if (is_embedded(evt->window)) {
|
|
|
|
m_log.trace("tray: Received map_notify");
|
|
|
|
m_log.trace("tray: Set client mapped");
|
|
|
|
find_client(evt->window)->mapped(true);
|
2017-01-01 14:29:38 -05:00
|
|
|
size_t clientcount{mapped_clients()};
|
|
|
|
if (clientcount > m_opts.configured_slots) {
|
2016-12-03 14:26:29 -05:00
|
|
|
reconfigure();
|
|
|
|
}
|
2017-01-09 12:49:22 -05:00
|
|
|
m_sig.emit(signals::ui_tray::mapped_clients{move(clientcount)});
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback : XCB_UNMAP_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::unmap_notify& evt) {
|
2016-12-05 14:41:00 -05:00
|
|
|
if (m_activated && evt->window == m_tray) {
|
2016-11-02 15:22:45 -04:00
|
|
|
m_log.trace("tray: Received unmap_notify");
|
2016-12-03 14:26:29 -05:00
|
|
|
m_log.trace("tray: Update container mapped flag");
|
|
|
|
m_mapped = false;
|
2016-12-05 14:41:00 -05:00
|
|
|
} else if (m_activated && is_embedded(evt->window)) {
|
|
|
|
m_log.trace("tray: Received unmap_notify");
|
|
|
|
m_log.trace("tray: Set client unmapped");
|
2017-01-01 14:29:38 -05:00
|
|
|
find_client(evt->window)->mapped(false);
|
|
|
|
m_sig.emit(signals::ui_tray::mapped_clients{mapped_clients()});
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
bool tray_manager::on(const visibility_change& evt) {
|
2017-01-01 14:29:38 -05:00
|
|
|
bool visible{evt.cast()};
|
2016-12-05 14:41:00 -05:00
|
|
|
size_t clients{mapped_clients()};
|
|
|
|
|
2016-12-15 15:00:17 -05:00
|
|
|
m_log.trace("tray: visibility_change (state=%i, activated=%i, mapped=%i, hidden=%i)", visible,
|
|
|
|
static_cast<bool>(m_activated), static_cast<bool>(m_mapped), static_cast<bool>(m_hidden));
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
m_hidden = !visible;
|
|
|
|
|
2016-12-15 15:00:17 -05:00
|
|
|
if (!m_activated) {
|
2016-12-05 14:41:00 -05:00
|
|
|
return false;
|
|
|
|
} else if (!m_hidden && !m_mapped && clients) {
|
|
|
|
m_connection.map_window(m_tray);
|
|
|
|
} else if ((!clients || m_hidden) && m_mapped) {
|
|
|
|
m_connection.unmap_window(m_tray);
|
|
|
|
} else if (m_mapped && !m_hidden && clients) {
|
|
|
|
redraw_window();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_connection.flush();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-16 01:10:45 -05:00
|
|
|
bool tray_manager::on(const dim_window& evt) {
|
|
|
|
if (m_activated) {
|
2017-01-01 14:29:38 -05:00
|
|
|
wm_util::set_wm_window_opacity(m_connection, m_tray, evt.cast() * 0xFFFFFFFF);
|
2016-12-20 22:50:43 -05:00
|
|
|
m_connection.flush();
|
2016-12-16 01:10:45 -05:00
|
|
|
}
|
|
|
|
// let the event bubble
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS_END
|