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.
|
||||
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/)
|
||||
- [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)
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
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")
|
||||
|
||||
# Strip disabled modules {{{
|
||||
|
@ -41,7 +41,7 @@ font-1 = unifont:size=6:heavy;-2
|
||||
font-2 = siji:pixelsize=10;0
|
||||
|
||||
modules-left = bspwm i3 mpd
|
||||
modules-center =
|
||||
modules-center = xwindow
|
||||
modules-right = backlight volume memory cpu wlan eth battery temperature date powermenu
|
||||
|
||||
tray-position = right
|
||||
@ -52,6 +52,12 @@ tray-padding = 4
|
||||
;wm-restack = bspwm
|
||||
|
||||
|
||||
[module/xwindow]
|
||||
type = internal/xwindow
|
||||
label = %title%
|
||||
label-maxlen = 30
|
||||
|
||||
|
||||
[module/filesystem]
|
||||
type = internal/fs
|
||||
interval = 25
|
||||
|
@ -52,6 +52,12 @@ tray-padding = 4
|
||||
;wm-restack = bspwm
|
||||
|
||||
|
||||
[module/xwindow]
|
||||
type = internal/xwindow
|
||||
label = %title%
|
||||
label-maxlen = 30
|
||||
|
||||
|
||||
[module/filesystem]
|
||||
type = internal/fs
|
||||
interval = 25
|
||||
|
@ -52,6 +52,7 @@ namespace drawtypes {
|
||||
operator bool();
|
||||
label_t clone();
|
||||
void reset_tokens();
|
||||
bool has_token(string token);
|
||||
void replace_token(string token, string replacement);
|
||||
void replace_defined_values(const label_t& label);
|
||||
void copy_undefined(const label_t& label);
|
||||
|
@ -32,6 +32,7 @@ namespace modules {
|
||||
using static_module::static_module;
|
||||
|
||||
void setup();
|
||||
void teardown();
|
||||
void handle(const evt::randr_notify& evt);
|
||||
void update();
|
||||
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) {
|
||||
return sizeof(p) / sizeof(p[0]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using malloc_ptr_t = shared_ptr<T>;
|
||||
}
|
||||
|
||||
LEMONBUDDY_NS_END
|
||||
|
@ -8,10 +8,14 @@ struct cached_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_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_DOCK;
|
||||
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:
|
||||
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(
|
||||
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_pos(int16_t x, int16_t y);
|
||||
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});
|
||||
|
||||
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");
|
||||
wm_util::set_wmpid(m_connection, m_window, getpid());
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "modules/text.hpp"
|
||||
#include "modules/unsupported.hpp"
|
||||
#include "modules/xbacklight.hpp"
|
||||
#include "modules/xwindow.hpp"
|
||||
|
||||
#include "components/bar.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));
|
||||
else if (type == "internal/backlight")
|
||||
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")
|
||||
module.reset(new battery_module(bar, m_log, m_conf, module_name));
|
||||
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));
|
||||
else if (type == "internal/temperature")
|
||||
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")
|
||||
module.reset(new text_module(bar, m_log, m_conf, module_name));
|
||||
else if (type == "custom/script")
|
||||
|
@ -20,6 +20,10 @@ namespace drawtypes {
|
||||
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) {
|
||||
m_tokenized = string_util::replace_all(m_tokenized, token, replacement);
|
||||
}
|
||||
|
@ -61,6 +61,13 @@ namespace modules {
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the event registry
|
||||
*/
|
||||
void xbacklight_module::teardown() {
|
||||
m_connection.detach_sink(this, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_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_DESKTOP;
|
||||
xcb_atom_t _NET_WM_VISIBLE_NAME;
|
||||
xcb_atom_t _NET_WM_WINDOW_TYPE;
|
||||
xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK;
|
||||
xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;
|
||||
@ -34,9 +38,13 @@ xcb_atom_t _XSETROOT_ID;
|
||||
xcb_atom_t ESETROOT_PMAP_ID;
|
||||
|
||||
// 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_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_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},
|
||||
|
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
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
auto conn = connection();
|
||||
|
||||
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};
|
||||
uint32_t values[16]{0};
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
uint32_t mask{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);
|
||||
|
||||
xutils::pack_values(mask, ¶ms, values);
|
||||
connection().configure_window_checked(*this, mask, values);
|
||||
configure_checked(mask, values);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigure the window position
|
||||
*/
|
||||
window window::reconfigure_pos(int16_t x, int16_t y) {
|
||||
uint32_t mask{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);
|
||||
|
||||
xutils::pack_values(mask, ¶ms, values);
|
||||
connection().configure_window_checked(*this, mask, values);
|
||||
configure_checked(mask, values);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigure the windows ewmh strut
|
||||
*/
|
||||
window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom) {
|
||||
auto& conn = connection();
|
||||
|
||||
@ -75,10 +110,14 @@ window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger redraw by toggling visibility state
|
||||
*/
|
||||
void window::redraw() {
|
||||
xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_FULLY_OBSCURED);
|
||||
xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_UNOBSCURED);
|
||||
connection().flush();
|
||||
auto conn = connection();
|
||||
xutils::visibility_notify(conn, *this, XCB_VISIBILITY_FULLY_OBSCURED);
|
||||
xutils::visibility_notify(conn, *this, XCB_VISIBILITY_UNOBSCURED);
|
||||
conn.flush();
|
||||
}
|
||||
|
||||
LEMONBUDDY_NS_END
|
||||
|
Loading…
Reference in New Issue
Block a user