feat(xwindow): New module "xwindow"
Add module to display title of active window. Requires WM with support for the _NET_ACTIVE_WINDOW hint. Ref #84
This commit is contained in:
parent
b921225487
commit
51d8f289fa
@ -34,6 +34,7 @@ problems by [creating an issue ticket](https://github.com/jaagr/lemonbuddy/issue
|
|||||||
The main purpose of **Lemonbuddy** is to help users create awesome status bars.
|
The main purpose of **Lemonbuddy** is to help users create awesome status bars.
|
||||||
It has built-in functionality to generate content for the most commonly used widgets, such as:
|
It has built-in functionality to generate content for the most commonly used widgets, such as:
|
||||||
|
|
||||||
|
- Window title
|
||||||
- Playback controls and status display for [MPD](https://www.musicpd.org/) using [libmpdclient](https://www.musicpd.org/libs/libmpdclient/)
|
- Playback controls and status display for [MPD](https://www.musicpd.org/) using [libmpdclient](https://www.musicpd.org/libs/libmpdclient/)
|
||||||
- [ALSA](http://www.alsa-project.org/main/index.php/Main_Page) volume controls
|
- [ALSA](http://www.alsa-project.org/main/index.php/Main_Page) volume controls
|
||||||
- Workspace and desktop panel for [bspwm](https://github.com/baskerville/bspwm) and [i3](https://github.com/i3/i3)
|
- Workspace and desktop panel for [bspwm](https://github.com/baskerville/bspwm) and [i3](https://github.com/i3/i3)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
set(MODULES_LEFT "bspwm i3 mpd")
|
set(MODULES_LEFT "bspwm i3 mpd")
|
||||||
set(MODULES_CENTER "")
|
set(MODULES_CENTER "xwindow")
|
||||||
set(MODULES_RIGHT "backlight volume memory cpu wlan eth battery temperature date powermenu")
|
set(MODULES_RIGHT "backlight volume memory cpu wlan eth battery temperature date powermenu")
|
||||||
|
|
||||||
# Strip disabled modules {{{
|
# Strip disabled modules {{{
|
||||||
|
@ -41,7 +41,7 @@ font-1 = unifont:size=6:heavy;-2
|
|||||||
font-2 = siji:pixelsize=10;0
|
font-2 = siji:pixelsize=10;0
|
||||||
|
|
||||||
modules-left = bspwm i3 mpd
|
modules-left = bspwm i3 mpd
|
||||||
modules-center =
|
modules-center = xwindow
|
||||||
modules-right = backlight volume memory cpu wlan eth battery temperature date powermenu
|
modules-right = backlight volume memory cpu wlan eth battery temperature date powermenu
|
||||||
|
|
||||||
tray-position = right
|
tray-position = right
|
||||||
@ -52,6 +52,12 @@ tray-padding = 4
|
|||||||
;wm-restack = bspwm
|
;wm-restack = bspwm
|
||||||
|
|
||||||
|
|
||||||
|
[module/xwindow]
|
||||||
|
type = internal/xwindow
|
||||||
|
label = %title%
|
||||||
|
label-maxlen = 30
|
||||||
|
|
||||||
|
|
||||||
[module/filesystem]
|
[module/filesystem]
|
||||||
type = internal/fs
|
type = internal/fs
|
||||||
interval = 25
|
interval = 25
|
||||||
|
@ -52,6 +52,12 @@ tray-padding = 4
|
|||||||
;wm-restack = bspwm
|
;wm-restack = bspwm
|
||||||
|
|
||||||
|
|
||||||
|
[module/xwindow]
|
||||||
|
type = internal/xwindow
|
||||||
|
label = %title%
|
||||||
|
label-maxlen = 30
|
||||||
|
|
||||||
|
|
||||||
[module/filesystem]
|
[module/filesystem]
|
||||||
type = internal/fs
|
type = internal/fs
|
||||||
interval = 25
|
interval = 25
|
||||||
|
@ -52,6 +52,7 @@ namespace drawtypes {
|
|||||||
operator bool();
|
operator bool();
|
||||||
label_t clone();
|
label_t clone();
|
||||||
void reset_tokens();
|
void reset_tokens();
|
||||||
|
bool has_token(string token);
|
||||||
void replace_token(string token, string replacement);
|
void replace_token(string token, string replacement);
|
||||||
void replace_defined_values(const label_t& label);
|
void replace_defined_values(const label_t& label);
|
||||||
void copy_undefined(const label_t& label);
|
void copy_undefined(const label_t& label);
|
||||||
|
@ -32,6 +32,7 @@ namespace modules {
|
|||||||
using static_module::static_module;
|
using static_module::static_module;
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
|
void teardown();
|
||||||
void handle(const evt::randr_notify& evt);
|
void handle(const evt::randr_notify& evt);
|
||||||
void update();
|
void update();
|
||||||
string get_output();
|
string get_output();
|
||||||
|
92
include/modules/xwindow.hpp
Normal file
92
include/modules/xwindow.hpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
#include "components/config.hpp"
|
||||||
|
#include "drawtypes/label.hpp"
|
||||||
|
#include "modules/meta.hpp"
|
||||||
|
#include "x11/connection.hpp"
|
||||||
|
#include "x11/ewmh.hpp"
|
||||||
|
#include "x11/icccm.hpp"
|
||||||
|
#include "x11/window.hpp"
|
||||||
|
|
||||||
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
/**
|
||||||
|
* Wrapper used to update the event mask of the
|
||||||
|
* currently active to enable title tracking
|
||||||
|
*/
|
||||||
|
class active_window {
|
||||||
|
public:
|
||||||
|
explicit active_window(xcb_window_t win)
|
||||||
|
: m_connection(configure_connection().create<decltype(m_connection)>()), m_window(m_connection, win) {
|
||||||
|
try {
|
||||||
|
m_window.change_event_mask(XCB_EVENT_MASK_PROPERTY_CHANGE);
|
||||||
|
} catch (const xpp::x::error::window& err) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~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 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 title(xcb_ewmh_connection_t* ewmh) {
|
||||||
|
string title;
|
||||||
|
|
||||||
|
if (!(title = ewmh_util::get_visible_name(ewmh, m_window)).empty()) {
|
||||||
|
return title;
|
||||||
|
} else if (!(title = icccm_util::get_wm_name(m_connection, m_window)).empty()) {
|
||||||
|
return title;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
connection& m_connection;
|
||||||
|
window m_window{m_connection};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module used to display information about the
|
||||||
|
* currently active X window.
|
||||||
|
*/
|
||||||
|
class xwindow_module : public static_module<xwindow_module>, public xpp::event::sink<evt::property_notify> {
|
||||||
|
public:
|
||||||
|
using static_module::static_module;
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
void teardown();
|
||||||
|
void handle(const evt::property_notify& evt);
|
||||||
|
void update();
|
||||||
|
string get_output();
|
||||||
|
bool build(builder* builder, string tag) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr auto TAG_LABEL = "<label>";
|
||||||
|
|
||||||
|
xcb_ewmh_connection_t m_ewmh;
|
||||||
|
xcb_timestamp_t m_timestamp;
|
||||||
|
unique_ptr<active_window> m_active;
|
||||||
|
|
||||||
|
label_t m_label;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
LEMONBUDDY_NS_END
|
@ -20,6 +20,9 @@ namespace memory_util {
|
|||||||
inline auto countof(T& p) {
|
inline auto countof(T& p) {
|
||||||
return sizeof(p) / sizeof(p[0]);
|
return sizeof(p) / sizeof(p[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using malloc_ptr_t = shared_ptr<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
LEMONBUDDY_NS_END
|
LEMONBUDDY_NS_END
|
||||||
|
@ -8,10 +8,14 @@ struct cached_atom {
|
|||||||
xcb_atom_t* atom;
|
xcb_atom_t* atom;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern cached_atom ATOMS[29];
|
extern cached_atom ATOMS[33];
|
||||||
|
|
||||||
|
extern xcb_atom_t _NET_SUPPORTED;
|
||||||
|
extern xcb_atom_t _NET_CURRENT_DESKTOP;
|
||||||
|
extern xcb_atom_t _NET_ACTIVE_WINDOW;
|
||||||
extern xcb_atom_t _NET_WM_NAME;
|
extern xcb_atom_t _NET_WM_NAME;
|
||||||
extern xcb_atom_t _NET_WM_DESKTOP;
|
extern xcb_atom_t _NET_WM_DESKTOP;
|
||||||
|
extern xcb_atom_t _NET_WM_VISIBLE_NAME;
|
||||||
extern xcb_atom_t _NET_WM_WINDOW_TYPE;
|
extern xcb_atom_t _NET_WM_WINDOW_TYPE;
|
||||||
extern xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK;
|
extern xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK;
|
||||||
extern xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;
|
extern xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;
|
||||||
|
21
include/x11/ewmh.hpp
Normal file
21
include/x11/ewmh.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xcb/xcb_ewmh.h>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "x11/connection.hpp"
|
||||||
|
|
||||||
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
namespace ewmh_util {
|
||||||
|
bool setup(connection& conn, xcb_ewmh_connection_t* dst);
|
||||||
|
bool supports(xcb_ewmh_connection_t* ewmh, xcb_atom_t atom);
|
||||||
|
|
||||||
|
xcb_window_t get_active_window(xcb_ewmh_connection_t* conn);
|
||||||
|
|
||||||
|
string get_visible_name(xcb_ewmh_connection_t* conn, xcb_window_t win);
|
||||||
|
string get_icon_name(xcb_ewmh_connection_t* conn, xcb_window_t win);
|
||||||
|
string get_reply_string(xcb_ewmh_get_utf8_strings_reply_t* reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
LEMONBUDDY_NS_END
|
15
include/x11/icccm.hpp
Normal file
15
include/x11/icccm.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xcb/xcb_icccm.h>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "x11/connection.hpp"
|
||||||
|
|
||||||
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
namespace icccm_util {
|
||||||
|
string get_wm_name(xcb_connection_t* conn, xcb_window_t win);
|
||||||
|
string get_reply_string(xcb_icccm_get_text_property_reply_t* reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
LEMONBUDDY_NS_END
|
@ -12,11 +12,19 @@ class window : public xpp::window<connection_t&> {
|
|||||||
public:
|
public:
|
||||||
using xpp::window<connection_t&>::window;
|
using xpp::window<connection_t&>::window;
|
||||||
|
|
||||||
explicit window(connection_t& conn) : xpp::window<connection_t&>(conn, conn.generate_id()) {}
|
explicit window(connection_t& conn) : xpp::window<connection_t&>(conn, XCB_NONE) {}
|
||||||
|
|
||||||
|
window& operator=(const xcb_window_t win) {
|
||||||
|
*this = window{connection(), win};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
window create_checked(
|
window create_checked(
|
||||||
int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask = 0, const xcb_params_cw_t* p = nullptr);
|
int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask = 0, const xcb_params_cw_t* p = nullptr);
|
||||||
|
|
||||||
|
window change_event_mask(uint32_t mask);
|
||||||
|
window ensure_event_mask(uint32_t event);
|
||||||
|
|
||||||
window reconfigure_geom(uint16_t w, uint16_t h, int16_t x = 0, int16_t y = 0);
|
window reconfigure_geom(uint16_t w, uint16_t h, int16_t x = 0, int16_t y = 0);
|
||||||
window reconfigure_pos(int16_t x, int16_t y);
|
window reconfigure_pos(int16_t x, int16_t y);
|
||||||
window reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom = false);
|
window reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom = false);
|
||||||
|
@ -722,7 +722,7 @@ void bar::set_wmhints() {
|
|||||||
wm_util::set_wmstate(m_connection, m_window, {_NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE});
|
wm_util::set_wmstate(m_connection, m_window, {_NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE});
|
||||||
|
|
||||||
m_log.trace("bar: Set _NET_WM_DESKTOP");
|
m_log.trace("bar: Set _NET_WM_DESKTOP");
|
||||||
wm_util::set_wmdesktop(m_connection, m_window, -1u);
|
wm_util::set_wmdesktop(m_connection, m_window, 0xFFFFFFFF);
|
||||||
|
|
||||||
m_log.trace("bar: Set _NET_WM_PID");
|
m_log.trace("bar: Set _NET_WM_PID");
|
||||||
wm_util::set_wmpid(m_connection, m_window, getpid());
|
wm_util::set_wmpid(m_connection, m_window, getpid());
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "modules/text.hpp"
|
#include "modules/text.hpp"
|
||||||
#include "modules/unsupported.hpp"
|
#include "modules/unsupported.hpp"
|
||||||
#include "modules/xbacklight.hpp"
|
#include "modules/xbacklight.hpp"
|
||||||
|
#include "modules/xwindow.hpp"
|
||||||
|
|
||||||
#include "components/bar.hpp"
|
#include "components/bar.hpp"
|
||||||
#include "components/config.hpp"
|
#include "components/config.hpp"
|
||||||
@ -358,8 +359,6 @@ void controller::bootstrap_modules() {
|
|||||||
module.reset(new counter_module(bar, m_log, m_conf, module_name));
|
module.reset(new counter_module(bar, m_log, m_conf, module_name));
|
||||||
else if (type == "internal/backlight")
|
else if (type == "internal/backlight")
|
||||||
module.reset(new backlight_module(bar, m_log, m_conf, module_name));
|
module.reset(new backlight_module(bar, m_log, m_conf, module_name));
|
||||||
else if (type == "internal/xbacklight")
|
|
||||||
module.reset(new xbacklight_module(bar, m_log, m_conf, module_name));
|
|
||||||
else if (type == "internal/battery")
|
else if (type == "internal/battery")
|
||||||
module.reset(new battery_module(bar, m_log, m_conf, module_name));
|
module.reset(new battery_module(bar, m_log, m_conf, module_name));
|
||||||
else if (type == "internal/bspwm")
|
else if (type == "internal/bspwm")
|
||||||
@ -382,6 +381,10 @@ void controller::bootstrap_modules() {
|
|||||||
module.reset(new network_module(bar, m_log, m_conf, module_name));
|
module.reset(new network_module(bar, m_log, m_conf, module_name));
|
||||||
else if (type == "internal/temperature")
|
else if (type == "internal/temperature")
|
||||||
module.reset(new temperature_module(bar, m_log, m_conf, module_name));
|
module.reset(new temperature_module(bar, m_log, m_conf, module_name));
|
||||||
|
else if (type == "internal/xbacklight")
|
||||||
|
module.reset(new xbacklight_module(bar, m_log, m_conf, module_name));
|
||||||
|
else if (type == "internal/xwindow")
|
||||||
|
module.reset(new xwindow_module(bar, m_log, m_conf, module_name));
|
||||||
else if (type == "custom/text")
|
else if (type == "custom/text")
|
||||||
module.reset(new text_module(bar, m_log, m_conf, module_name));
|
module.reset(new text_module(bar, m_log, m_conf, module_name));
|
||||||
else if (type == "custom/script")
|
else if (type == "custom/script")
|
||||||
|
@ -20,6 +20,10 @@ namespace drawtypes {
|
|||||||
m_tokenized = m_text;
|
m_tokenized = m_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool label::has_token(string token) {
|
||||||
|
return m_text.find(token) != string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
void label::replace_token(string token, string replacement) {
|
void label::replace_token(string token, string replacement) {
|
||||||
m_tokenized = string_util::replace_all(m_tokenized, token, replacement);
|
m_tokenized = string_util::replace_all(m_tokenized, token, replacement);
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,13 @@ namespace modules {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from the event registry
|
||||||
|
*/
|
||||||
|
void xbacklight_module::teardown() {
|
||||||
|
m_connection.detach_sink(this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for XCB_RANDR_NOTIFY events
|
* Handler for XCB_RANDR_NOTIFY events
|
||||||
*/
|
*/
|
||||||
|
123
src/modules/xwindow.cpp
Normal file
123
src/modules/xwindow.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#include "modules/xwindow.hpp"
|
||||||
|
#include "x11/atoms.hpp"
|
||||||
|
#include "x11/graphics.hpp"
|
||||||
|
|
||||||
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
/**
|
||||||
|
* Bootstrap the module
|
||||||
|
*/
|
||||||
|
void xwindow_module::setup() {
|
||||||
|
connection& conn{configure_connection().create<decltype(conn)>()};
|
||||||
|
|
||||||
|
// Initialize ewmh atoms
|
||||||
|
if (!ewmh_util::setup(conn, &m_ewmh)) {
|
||||||
|
throw module_error("Failed to initialize ewmh atoms");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the WM supports _NET_ACTIVE_WINDOW
|
||||||
|
if (!ewmh_util::supports(&m_ewmh, _NET_ACTIVE_WINDOW)) {
|
||||||
|
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
|
||||||
|
window root{conn, conn.root()};
|
||||||
|
root.ensure_event_mask(XCB_EVENT_MASK_PROPERTY_CHANGE);
|
||||||
|
|
||||||
|
// Connect with the event registry
|
||||||
|
conn.attach_sink(this, 1);
|
||||||
|
|
||||||
|
// Trigger the initial draw event
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from the event registry
|
||||||
|
*/
|
||||||
|
void xwindow_module::teardown() {
|
||||||
|
connection& conn{configure_connection().create<decltype(conn)>()};
|
||||||
|
conn.detach_sink(this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for XCB_PROPERTY_NOTIFY events
|
||||||
|
*/
|
||||||
|
void xwindow_module::handle(const evt::property_notify& evt) {
|
||||||
|
if (evt->time <= m_timestamp) {
|
||||||
|
return;
|
||||||
|
} else if (evt->atom == _NET_ACTIVE_WINDOW) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timestamp = evt->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the currently active window and query its title
|
||||||
|
*/
|
||||||
|
void xwindow_module::update() {
|
||||||
|
xcb_window_t win{ewmh_util::get_active_window(&m_ewmh)};
|
||||||
|
string title;
|
||||||
|
|
||||||
|
if (m_active && m_active->match(win)) {
|
||||||
|
title = m_active->title(&m_ewmh);
|
||||||
|
} else if (win != XCB_NONE) {
|
||||||
|
m_active = make_unique<active_window>(win);
|
||||||
|
title = m_active->title(&m_ewmh);
|
||||||
|
} else {
|
||||||
|
m_active.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_label) {
|
||||||
|
m_label->reset_tokens();
|
||||||
|
m_label->replace_token("%title%", title);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit notification to trigger redraw
|
||||||
|
broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the module output
|
||||||
|
*/
|
||||||
|
string xwindow_module::get_output() {
|
||||||
|
m_builder->node(static_module::get_output());
|
||||||
|
return m_builder->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output content as defined in the config
|
||||||
|
*/
|
||||||
|
bool xwindow_module::build(builder* builder, string tag) const {
|
||||||
|
if (tag == TAG_LABEL) {
|
||||||
|
builder->node(m_label);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LEMONBUDDY_NS_END
|
@ -3,8 +3,12 @@
|
|||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xcb_atom.h>
|
#include <xcb/xcb_atom.h>
|
||||||
|
|
||||||
|
xcb_atom_t _NET_SUPPORTED;
|
||||||
|
xcb_atom_t _NET_CURRENT_DESKTOP;
|
||||||
|
xcb_atom_t _NET_ACTIVE_WINDOW;
|
||||||
xcb_atom_t _NET_WM_NAME;
|
xcb_atom_t _NET_WM_NAME;
|
||||||
xcb_atom_t _NET_WM_DESKTOP;
|
xcb_atom_t _NET_WM_DESKTOP;
|
||||||
|
xcb_atom_t _NET_WM_VISIBLE_NAME;
|
||||||
xcb_atom_t _NET_WM_WINDOW_TYPE;
|
xcb_atom_t _NET_WM_WINDOW_TYPE;
|
||||||
xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK;
|
xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK;
|
||||||
xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;
|
xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;
|
||||||
@ -34,9 +38,13 @@ xcb_atom_t _XSETROOT_ID;
|
|||||||
xcb_atom_t ESETROOT_PMAP_ID;
|
xcb_atom_t ESETROOT_PMAP_ID;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
cached_atom ATOMS[29] = {
|
cached_atom ATOMS[33] = {
|
||||||
|
{"_NET_SUPPORTED", sizeof("_NET_SUPPORTED") - 1, &_NET_SUPPORTED},
|
||||||
|
{"_NET_CURRENT_DESKTOP", sizeof("_NET_CURRENT_DESKTOP") - 1, &_NET_CURRENT_DESKTOP},
|
||||||
|
{"_NET_ACTIVE_WINDOW", sizeof("_NET_ACTIVE_WINDOW") - 1, &_NET_ACTIVE_WINDOW},
|
||||||
{"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &_NET_WM_NAME},
|
{"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &_NET_WM_NAME},
|
||||||
{"_NET_WM_DESKTOP", sizeof("_NET_WM_DESKTOP") - 1, &_NET_WM_DESKTOP},
|
{"_NET_WM_DESKTOP", sizeof("_NET_WM_DESKTOP") - 1, &_NET_WM_DESKTOP},
|
||||||
|
{"_NET_WM_VISIBLE_NAME", sizeof("_NET_WM_VISIBLE_NAME") - 1, &_NET_WM_VISIBLE_NAME},
|
||||||
{"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1, &_NET_WM_WINDOW_TYPE},
|
{"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1, &_NET_WM_WINDOW_TYPE},
|
||||||
{"_NET_WM_WINDOW_TYPE_DOCK", sizeof("_NET_WM_WINDOW_TYPE_DOCK") - 1, &_NET_WM_WINDOW_TYPE_DOCK},
|
{"_NET_WM_WINDOW_TYPE_DOCK", sizeof("_NET_WM_WINDOW_TYPE_DOCK") - 1, &_NET_WM_WINDOW_TYPE_DOCK},
|
||||||
{"_NET_WM_WINDOW_TYPE_NORMAL", sizeof("_NET_WM_WINDOW_TYPE_NORMAL") - 1, &_NET_WM_WINDOW_TYPE_NORMAL},
|
{"_NET_WM_WINDOW_TYPE_NORMAL", sizeof("_NET_WM_WINDOW_TYPE_NORMAL") - 1, &_NET_WM_WINDOW_TYPE_NORMAL},
|
||||||
|
64
src/x11/ewmh.cpp
Normal file
64
src/x11/ewmh.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "x11/ewmh.hpp"
|
||||||
|
|
||||||
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
namespace ewmh_util {
|
||||||
|
bool setup(connection& conn, xcb_ewmh_connection_t* dst) {
|
||||||
|
return xcb_ewmh_init_atoms_replies(dst, xcb_ewmh_init_atoms(conn, dst), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool supports(xcb_ewmh_connection_t* ewmh, xcb_atom_t atom) {
|
||||||
|
bool supports{false};
|
||||||
|
|
||||||
|
xcb_ewmh_get_atoms_reply_t reply;
|
||||||
|
reply.atoms = nullptr;
|
||||||
|
|
||||||
|
if (!xcb_ewmh_get_supported_reply(ewmh, xcb_ewmh_get_supported(ewmh, 0), &reply, nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t n = 0; n < reply.atoms_len; ++n) {
|
||||||
|
if (reply.atoms[n] == atom) {
|
||||||
|
supports = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply.atoms != nullptr) {
|
||||||
|
xcb_ewmh_get_atoms_reply_wipe(&reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
return supports;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_window_t get_active_window(xcb_ewmh_connection_t* conn) {
|
||||||
|
xcb_window_t win{XCB_NONE};
|
||||||
|
xcb_ewmh_get_active_window_reply(conn, xcb_ewmh_get_active_window(conn, 0), &win, nullptr);
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_visible_name(xcb_ewmh_connection_t* conn, xcb_window_t win) {
|
||||||
|
xcb_ewmh_get_utf8_strings_reply_t utf8_reply;
|
||||||
|
if (!xcb_ewmh_get_wm_visible_name_reply(conn, xcb_ewmh_get_wm_visible_name(conn, win), &utf8_reply, nullptr))
|
||||||
|
return "";
|
||||||
|
return get_reply_string(&utf8_reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_icon_name(xcb_ewmh_connection_t* conn, xcb_window_t win) {
|
||||||
|
xcb_ewmh_get_utf8_strings_reply_t utf8_reply;
|
||||||
|
if (!xcb_ewmh_get_wm_icon_name_reply(conn, xcb_ewmh_get_wm_icon_name(conn, win), &utf8_reply, nullptr))
|
||||||
|
return "";
|
||||||
|
return get_reply_string(&utf8_reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_reply_string(xcb_ewmh_get_utf8_strings_reply_t* reply) {
|
||||||
|
if (reply == nullptr || !reply->strings_len)
|
||||||
|
return "";
|
||||||
|
char buffer[BUFSIZ]{'\0'};
|
||||||
|
strncpy(buffer, reply->strings, reply->strings_len);
|
||||||
|
xcb_ewmh_get_utf8_strings_reply_wipe(reply);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LEMONBUDDY_NS_END
|
23
src/x11/icccm.cpp
Normal file
23
src/x11/icccm.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "x11/icccm.hpp"
|
||||||
|
|
||||||
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
namespace icccm_util {
|
||||||
|
string get_wm_name(xcb_connection_t* conn, xcb_window_t win) {
|
||||||
|
xcb_icccm_get_text_property_reply_t reply;
|
||||||
|
if (!xcb_icccm_get_wm_name_reply(conn, xcb_icccm_get_wm_name(conn, win), &reply, nullptr))
|
||||||
|
return "";
|
||||||
|
return get_reply_string(&reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_reply_string(xcb_icccm_get_text_property_reply_t* reply) {
|
||||||
|
if (reply->name == nullptr || !reply->name_len)
|
||||||
|
return "";
|
||||||
|
char buffer[BUFSIZ]{'\0'};
|
||||||
|
strncpy(buffer, reply->name, reply->name_len);
|
||||||
|
xcb_icccm_get_text_property_reply_wipe(reply);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LEMONBUDDY_NS_END
|
@ -9,20 +9,49 @@
|
|||||||
|
|
||||||
LEMONBUDDY_NS
|
LEMONBUDDY_NS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create window and check for errors
|
||||||
|
*/
|
||||||
window window::create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p) {
|
window window::create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p) {
|
||||||
|
auto conn = connection();
|
||||||
|
|
||||||
if (*this == XCB_NONE) {
|
if (*this == XCB_NONE) {
|
||||||
resource(connection(), connection().generate_id());
|
*this = conn.generate_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto root{connection().screen()->root};
|
auto root{conn.screen()->root};
|
||||||
auto copy{XCB_COPY_FROM_PARENT};
|
auto copy{XCB_COPY_FROM_PARENT};
|
||||||
uint32_t values[16]{0};
|
uint32_t values[16]{0};
|
||||||
xutils::pack_values(mask, p, values);
|
xutils::pack_values(mask, p, values);
|
||||||
connection().create_window_checked(copy, *this, root, x, y, w, h, 0, copy, copy, mask, values);
|
conn.create_window_checked(copy, *this, root, x, y, w, h, 0, copy, copy, mask, values);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the window event mask
|
||||||
|
*/
|
||||||
|
window window::change_event_mask(uint32_t mask) {
|
||||||
|
change_attributes_checked(XCB_CW_EVENT_MASK, &mask);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add given event to the event mask unless already added
|
||||||
|
*/
|
||||||
|
window window::ensure_event_mask(uint32_t event) {
|
||||||
|
auto attributes = get_attributes();
|
||||||
|
|
||||||
|
if ((attributes->your_event_mask & event) != event) {
|
||||||
|
change_event_mask(attributes->your_event_mask | event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconfigure the window geometry
|
||||||
|
*/
|
||||||
window window::reconfigure_geom(uint16_t w, uint16_t h, int16_t x, int16_t y) {
|
window window::reconfigure_geom(uint16_t w, uint16_t h, int16_t x, int16_t y) {
|
||||||
uint32_t mask{0};
|
uint32_t mask{0};
|
||||||
uint32_t values[7]{0};
|
uint32_t values[7]{0};
|
||||||
@ -34,11 +63,14 @@ window window::reconfigure_geom(uint16_t w, uint16_t h, int16_t x, int16_t y) {
|
|||||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, y, y);
|
XCB_AUX_ADD_PARAM(&mask, ¶ms, y, y);
|
||||||
|
|
||||||
xutils::pack_values(mask, ¶ms, values);
|
xutils::pack_values(mask, ¶ms, values);
|
||||||
connection().configure_window_checked(*this, mask, values);
|
configure_checked(mask, values);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconfigure the window position
|
||||||
|
*/
|
||||||
window window::reconfigure_pos(int16_t x, int16_t y) {
|
window window::reconfigure_pos(int16_t x, int16_t y) {
|
||||||
uint32_t mask{0};
|
uint32_t mask{0};
|
||||||
uint32_t values[2]{0};
|
uint32_t values[2]{0};
|
||||||
@ -48,11 +80,14 @@ window window::reconfigure_pos(int16_t x, int16_t y) {
|
|||||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, y, y);
|
XCB_AUX_ADD_PARAM(&mask, ¶ms, y, y);
|
||||||
|
|
||||||
xutils::pack_values(mask, ¶ms, values);
|
xutils::pack_values(mask, ¶ms, values);
|
||||||
connection().configure_window_checked(*this, mask, values);
|
configure_checked(mask, values);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconfigure the windows ewmh strut
|
||||||
|
*/
|
||||||
window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom) {
|
window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom) {
|
||||||
auto& conn = connection();
|
auto& conn = connection();
|
||||||
|
|
||||||
@ -75,10 +110,14 @@ window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger redraw by toggling visibility state
|
||||||
|
*/
|
||||||
void window::redraw() {
|
void window::redraw() {
|
||||||
xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_FULLY_OBSCURED);
|
auto conn = connection();
|
||||||
xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_UNOBSCURED);
|
xutils::visibility_notify(conn, *this, XCB_VISIBILITY_FULLY_OBSCURED);
|
||||||
connection().flush();
|
xutils::visibility_notify(conn, *this, XCB_VISIBILITY_UNOBSCURED);
|
||||||
|
conn.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
LEMONBUDDY_NS_END
|
LEMONBUDDY_NS_END
|
||||||
|
Loading…
Reference in New Issue
Block a user