2016-11-19 03:03:18 +00:00
|
|
|
#include "modules/xwindow.hpp"
|
2016-11-19 14:49:03 +00:00
|
|
|
#include "drawtypes/label.hpp"
|
2016-12-09 08:02:47 +00:00
|
|
|
#include "utils/factory.hpp"
|
2016-11-19 03:03:18 +00:00
|
|
|
#include "x11/atoms.hpp"
|
2016-11-20 22:04:31 +00:00
|
|
|
#include "x11/connection.hpp"
|
2016-11-19 03:03:18 +00:00
|
|
|
#include "x11/graphics.hpp"
|
|
|
|
|
2016-11-20 22:04:31 +00:00
|
|
|
#include "modules/meta/base.inl"
|
|
|
|
|
2016-11-19 05:22:44 +00:00
|
|
|
POLYBAR_NS
|
2016-11-19 03:03:18 +00:00
|
|
|
|
|
|
|
namespace modules {
|
2016-11-20 22:04:31 +00:00
|
|
|
template class module<xwindow_module>;
|
|
|
|
|
2016-12-01 07:35:59 +00:00
|
|
|
/**
|
|
|
|
* Wrapper used to update the event mask of the
|
|
|
|
* currently active to enable title tracking
|
|
|
|
*/
|
2016-12-09 08:02:47 +00:00
|
|
|
active_window::active_window(xcb_window_t win) : m_connection(connection::make()), m_window(m_connection, win) {
|
2016-12-01 07:35:59 +00:00
|
|
|
try {
|
|
|
|
m_window.change_event_mask(XCB_EVENT_MASK_PROPERTY_CHANGE);
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deconstruct window object
|
|
|
|
*/
|
|
|
|
active_window::~active_window() {
|
|
|
|
try {
|
|
|
|
m_window.change_event_mask(XCB_EVENT_MASK_NO_EVENT);
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if current window matches passed value
|
|
|
|
*/
|
|
|
|
bool active_window::match(const xcb_window_t win) const {
|
|
|
|
return m_window == win;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the title by returning the first non-empty value of:
|
|
|
|
* _NET_WM_VISIBLE_NAME
|
|
|
|
* _NET_WM_NAME
|
|
|
|
*/
|
|
|
|
string active_window::title(xcb_ewmh_connection_t* ewmh) const {
|
|
|
|
string title;
|
|
|
|
|
|
|
|
if (!(title = ewmh_util::get_visible_name(ewmh, m_window)).empty()) {
|
|
|
|
return title;
|
|
|
|
} else if (!(title = ewmh_util::get_wm_name(ewmh, m_window)).empty()) {
|
|
|
|
return title;
|
|
|
|
} else if (!(title = icccm_util::get_wm_name(m_connection, m_window)).empty()) {
|
|
|
|
return title;
|
|
|
|
} else {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct module
|
|
|
|
*/
|
2016-12-21 07:00:09 +00:00
|
|
|
xwindow_module::xwindow_module(const bar_settings& bar, string name_)
|
|
|
|
: static_module<xwindow_module>(bar, move(name_)), m_connection(connection::make()) {
|
2016-11-19 03:03:18 +00:00
|
|
|
// Initialize ewmh atoms
|
2016-11-26 05:13:20 +00:00
|
|
|
if ((m_ewmh = ewmh_util::initialize()) == nullptr) {
|
2016-11-19 03:03:18 +00:00
|
|
|
throw module_error("Failed to initialize ewmh atoms");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the WM supports _NET_ACTIVE_WINDOW
|
2016-11-26 05:13:20 +00:00
|
|
|
if (!ewmh_util::supports(m_ewmh.get(), _NET_ACTIVE_WINDOW)) {
|
2016-11-19 03:03:18 +00:00
|
|
|
throw module_error("The WM does not list _NET_ACTIVE_WINDOW as a supported hint");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add formats and elements
|
|
|
|
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL});
|
|
|
|
|
|
|
|
if (m_formatter->has(TAG_LABEL)) {
|
|
|
|
m_label = load_optional_label(m_conf, name(), TAG_LABEL, "%title%");
|
|
|
|
}
|
|
|
|
|
|
|
|
// No need to setup X components if we can't show the title
|
|
|
|
if (!m_label || !m_label->has_token("%title%")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we get notified when root properties change
|
2016-12-01 07:35:59 +00:00
|
|
|
m_connection.ensure_event_mask(m_connection.root(), XCB_EVENT_MASK_PROPERTY_CHANGE);
|
2016-11-19 03:03:18 +00:00
|
|
|
|
|
|
|
// Connect with the event registry
|
2016-12-03 12:00:40 +00:00
|
|
|
m_connection.attach_sink(this, SINK_PRIORITY_MODULE);
|
2016-11-19 03:03:18 +00:00
|
|
|
|
|
|
|
// Trigger the initial draw event
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disconnect from the event registry
|
|
|
|
*/
|
|
|
|
void xwindow_module::teardown() {
|
2016-12-03 12:00:40 +00:00
|
|
|
m_connection.detach_sink(this, SINK_PRIORITY_MODULE);
|
2016-11-19 03:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for XCB_PROPERTY_NOTIFY events
|
|
|
|
*/
|
|
|
|
void xwindow_module::handle(const evt::property_notify& evt) {
|
2016-11-19 03:34:46 +00:00
|
|
|
if (evt->atom == _NET_ACTIVE_WINDOW) {
|
2016-11-19 03:03:18 +00:00
|
|
|
update();
|
|
|
|
} else if (evt->atom == _NET_CURRENT_DESKTOP) {
|
|
|
|
update();
|
|
|
|
} else if (evt->atom == _NET_WM_VISIBLE_NAME) {
|
|
|
|
update();
|
|
|
|
} else if (evt->atom == _NET_WM_NAME) {
|
|
|
|
update();
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the currently active window and query its title
|
|
|
|
*/
|
|
|
|
void xwindow_module::update() {
|
2016-11-26 05:13:20 +00:00
|
|
|
xcb_window_t win{ewmh_util::get_active_window(m_ewmh.get())};
|
2016-11-19 03:03:18 +00:00
|
|
|
string title;
|
|
|
|
|
|
|
|
if (m_active && m_active->match(win)) {
|
2016-11-26 05:13:20 +00:00
|
|
|
title = m_active->title(m_ewmh.get());
|
2016-11-19 03:03:18 +00:00
|
|
|
} else if (win != XCB_NONE) {
|
2016-12-09 08:02:47 +00:00
|
|
|
m_active = factory_util::unique<active_window>(win);
|
2016-11-26 05:13:20 +00:00
|
|
|
title = m_active->title(m_ewmh.get());
|
2016-11-19 03:03:18 +00:00
|
|
|
} else {
|
|
|
|
m_active.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_label) {
|
|
|
|
m_label->reset_tokens();
|
|
|
|
m_label->replace_token("%title%", title);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit notification to trigger redraw
|
|
|
|
broadcast();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Output content as defined in the config
|
|
|
|
*/
|
2016-11-25 12:55:15 +00:00
|
|
|
bool xwindow_module::build(builder* builder, const string& tag) const {
|
2016-11-19 03:03:18 +00:00
|
|
|
if (tag == TAG_LABEL) {
|
|
|
|
builder->node(m_label);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-19 05:22:44 +00:00
|
|
|
POLYBAR_NS_END
|