2019-10-27 17:27:58 -04:00
|
|
|
#include "x11/tray_manager.hpp"
|
|
|
|
|
2016-11-04 13:50:33 -04:00
|
|
|
#include <xcb/xcb_image.h>
|
2019-10-27 17:27:58 -04:00
|
|
|
|
2016-11-20 17:04:31 -05:00
|
|
|
#include <thread>
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2017-05-23 15:32:34 -04:00
|
|
|
#include "cairo/context.hpp"
|
|
|
|
#include "cairo/surface.hpp"
|
2016-12-05 14:41:00 -05:00
|
|
|
#include "components/config.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"
|
Add units support (POINT, PIXEL, SPACE) (#2578)
* add units support (POINT, PIXEL, SPACE) for polybar
- add a size_with_unit struct
- add a geometry_format_values struct
- move dpi initialisation from renderer.cpp to bar.cpp
- add a string to size_with_unit converter
- add point support (with pt)
- add pixel support (with px)
* Fix unit test compilation
* clang-format
* Better names
The old names didn't really capture the purpose of the structs and
function.
space_type -> spacing_type
space_size -> spacing_val
size_type -> extent_type
geometry -> extent_val
geometry_format_values -> percentage_with_offset
* Remove parse_size_with_unit
No longer needed. The convert<spacing_val> function in config.cpp
already does all the work for us and always setting the type to pixel
was wrong.
In addition, line-size should not be of type spacing_val but extent_val.
* Cleanup
I tried to address most of my comments on the old PR
* Fix renderer width calculation
We can't just blindly add the x difference to the width because for
example the width should increase if x < width and the increase keeps
x < width.
Similarly, we can't just add the offset to the width.
* Rename geom_format_to_pixels to percentage_with_offset_to_pixel
* Cleanup
* Apply suggested changes from Patrick on GitHub
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/bar.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/config.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/builder.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/builder.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* config: Use stod for parsing percentage
* Use stof instead of strtof
* units: Fix test edge cases
* Remove unnecessary clang-format toggle
* Use percentage_with_offset for margin-{top,bottom}
* Support negative extent values
* Rename unit to units and create a cpp file
* Move percentage_with_offset_to_pixel unit test to units
* Add unit tests for units_utils
* Clarify when and how negative spacing/extent is allowed
Negative spacing is never allowed and produces a config error.
Extents allow negative values in theory, but only a few use-cases accept
it.
Only the extent value used for the `%{O}` tag and the offset value in
percentage_with_offset can be negative. Everything else is capped below
at 0.
The final pixel value of percentage_with_offset also caps below at 0.
* Fix parsing errors not being caught in config
* Print a proper error message for uncaught exceptions
* Cleanup module::get_output
All changes preserve the existing semantics
* Stop using remove_trailing_space in module::get_output
Instead, we first check if the current tag is built, and only if it is,
the spacing is prepended.
* Remove unused imports
* Restore old behavior
If there are two tags and the second one isn't built (module::build
returns false), the space in between them is removed.
For example in the mpd module:
format-online = <toggle> <label-song> foo
If mpd is not running, the mpd module will return false when trying to
build the `<label-song>` tag. If we don't remove the space between
`<toggle>` and `<label-song>`, we end up with two spaces between
`<toggle>` and `foo`.
This change is to match the old behavior where at least one trailing
space character was removed from the builder.
* Add changelog entry
* Remove unused setting
* Use percentage with offset for tray-offset
Co-authored-by: Jérôme BOULMIER <jerome.boulmier@outlook.fr>
Co-authored-by: Joe Groocock <github@frebib.net>
2022-02-20 15:08:57 -05:00
|
|
|
#include "utils/units.hpp"
|
2017-01-24 02:49:27 -05:00
|
|
|
#include "x11/ewmh.hpp"
|
|
|
|
#include "x11/icccm.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-12 06:56:39 -05:00
|
|
|
#include "x11/xembed.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2021-09-08 12:13:21 -04:00
|
|
|
/*
|
|
|
|
* Tray implementation according to the System Tray Protocol.
|
|
|
|
*
|
|
|
|
* Ref: https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
|
2022-09-03 15:33:22 -04:00
|
|
|
*
|
|
|
|
* This class manages embedded tray icons by placing them on the bar in the correct position; the position itself is
|
|
|
|
* requested by the renderer.
|
|
|
|
*
|
|
|
|
* The tray manager needs to trigger bar updates only when the size of the entire tray changes (e.g. when tray icons are
|
|
|
|
* added/removed). EVerything else can be handled without an update.
|
|
|
|
*
|
|
|
|
* TODO
|
|
|
|
* * Better handling of state:
|
|
|
|
* * Differentiate between, inactive, active, and waiting for selection
|
2021-09-08 12:13:21 -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
|
|
|
|
2022-07-25 18:01:21 -04:00
|
|
|
tray_manager::tray_manager(
|
2022-09-03 15:33:22 -04:00
|
|
|
connection& conn, signal_emitter& emitter, const logger& logger, const bar_settings& bar_opts, on_update on_update)
|
|
|
|
: m_connection(conn), m_sig(emitter), m_log(logger), m_bar_opts(bar_opts), m_on_update(on_update) {
|
2016-12-14 05:34:09 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-09-03 14:56:32 -04:00
|
|
|
void tray_manager::setup(const config& conf, const string& section_name) {
|
|
|
|
unsigned client_height = m_bar_opts.inner_area().height;
|
2022-08-27 17:02:34 -04:00
|
|
|
|
2022-09-03 14:56:32 -04:00
|
|
|
// Add user-defined padding
|
|
|
|
m_opts.spacing = conf.get<unsigned>(section_name, "tray-padding", 0);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-09-03 14:56:32 -04:00
|
|
|
auto maxsize = conf.get<unsigned>(section_name, "tray-maxsize", 16);
|
2022-02-27 15:36:16 -05:00
|
|
|
if (client_height > maxsize) {
|
|
|
|
m_opts.spacing += (client_height - maxsize) / 2;
|
|
|
|
client_height = maxsize;
|
2016-12-05 14:41:00 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Apply user-defined scaling
|
2022-09-03 14:56:32 -04:00
|
|
|
auto scale = conf.get(section_name, "tray-scale", 1.0);
|
2022-08-28 08:49:09 -04:00
|
|
|
client_height *= scale;
|
|
|
|
|
|
|
|
m_opts.client_size = {client_height, client_height};
|
2022-02-27 15:36:16 -05:00
|
|
|
|
2021-11-17 05:41:40 -05:00
|
|
|
// Set user-defined foreground and background colors.
|
2022-08-27 17:02:34 -04:00
|
|
|
// TODO maybe remove
|
2022-09-03 14:56:32 -04:00
|
|
|
m_opts.background = conf.get(section_name, "tray-background", m_bar_opts.background);
|
|
|
|
m_opts.foreground = conf.get(section_name, "tray-foreground", m_bar_opts.foreground);
|
2016-11-25 07:55:15 -05:00
|
|
|
|
2022-08-27 17:02:34 -04:00
|
|
|
m_opts.selection_owner = m_bar_opts.x_data.window;
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2022-08-28 09:15:48 -04:00
|
|
|
if (m_bar_opts.x_data.window == XCB_NONE) {
|
|
|
|
m_log.err("tray: No bar window found, disabling tray");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:00 -05:00
|
|
|
// Activate the tray manager
|
|
|
|
query_atom();
|
|
|
|
activate();
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2022-08-28 08:49:09 -04:00
|
|
|
bool tray_manager::running() const {
|
|
|
|
return m_activated;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-12 06:56:39 -05:00
|
|
|
|
2022-08-28 09:29:36 -04:00
|
|
|
int tray_manager::get_width() const {
|
|
|
|
return m_tray_width;
|
|
|
|
}
|
|
|
|
|
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_sig.attach(this);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-12 06:56:39 -05:00
|
|
|
try {
|
2016-12-05 14:41:00 -05:00
|
|
|
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-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_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-25 07:55:15 -05:00
|
|
|
m_acquired_selection = 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
|
|
|
|
2022-09-03 15:33:22 -04:00
|
|
|
reconfigure_window();
|
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() {
|
2022-08-27 17:02:34 -04:00
|
|
|
if (!m_opts.selection_owner) {
|
2016-11-02 15:22:45 -04:00
|
|
|
return;
|
2022-08-28 08:56:56 -04:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-09-03 15:33:22 -04:00
|
|
|
reconfigure_window();
|
2016-11-12 06:56:39 -05:00
|
|
|
|
2022-08-28 08:56:56 -04:00
|
|
|
try {
|
|
|
|
reconfigure_clients();
|
|
|
|
} catch (const exception& err) {
|
|
|
|
m_log.err("Failed to reconfigure tray clients (%s)", err.what());
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-08-28 08:56:56 -04:00
|
|
|
m_connection.flush();
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reconfigure container window
|
2022-09-03 15:33:22 -04:00
|
|
|
*
|
|
|
|
* TODO should we call update_width directly?
|
2016-11-02 15:22:45 -04:00
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::reconfigure_window() {
|
2022-08-28 08:49:09 -04:00
|
|
|
m_log.trace("tray: Reconfigure window (hidden=%i, clients=%i)", static_cast<bool>(m_hidden), m_clients.size());
|
2022-09-03 15:33:22 -04:00
|
|
|
update_width();
|
2022-08-28 09:29:36 -04:00
|
|
|
}
|
|
|
|
|
2022-09-03 15:33:22 -04:00
|
|
|
/**
|
|
|
|
* TODO make sure this is always called when m_clients changes
|
|
|
|
*/
|
|
|
|
void tray_manager::update_width() {
|
|
|
|
unsigned new_width = calculate_w();
|
2022-08-28 09:29:36 -04:00
|
|
|
if (m_tray_width != new_width) {
|
|
|
|
m_tray_width = new_width;
|
2022-09-03 15:33:22 -04:00
|
|
|
m_on_update();
|
2022-08-28 09:29:36 -04:00
|
|
|
}
|
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");
|
|
|
|
|
2022-08-27 17:02:34 -04:00
|
|
|
int x = calculate_x() + m_opts.spacing;
|
2016-11-02 15:22:45 -04:00
|
|
|
|
|
|
|
for (auto it = m_clients.rbegin(); it != m_clients.rend(); it++) {
|
|
|
|
try {
|
2022-03-07 06:20:05 -05:00
|
|
|
it->ensure_state();
|
|
|
|
it->reconfigure(x, calculate_client_y());
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-02-27 15:36:16 -05:00
|
|
|
x += m_opts.client_size.w + m_opts.spacing;
|
2016-11-02 15:22:45 -04:00
|
|
|
} catch (const xpp::x::error::window& err) {
|
2022-03-07 06:20:05 -05:00
|
|
|
// TODO print error
|
2022-08-28 08:49:09 -04:00
|
|
|
m_log.err("Failed to reconfigure client (%s), removing ... (%s)", m_connection.id(it->client()), err.what());
|
2022-03-07 06:20:05 -05:00
|
|
|
remove_client(*it, 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
|
|
|
|
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() {
|
2022-08-28 08:56:56 -04:00
|
|
|
if (!m_activated || m_hidden) {
|
2016-11-04 13:50:33 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-28 08:56:56 -04:00
|
|
|
m_log.trace("tray: Refreshing clients");
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2022-03-07 06:20:05 -05:00
|
|
|
for (auto& client : m_clients) {
|
2021-05-18 05:05:02 -04:00
|
|
|
try {
|
2022-03-07 06:20:05 -05:00
|
|
|
if (client.mapped()) {
|
|
|
|
client.clear_window();
|
2021-05-18 05:05:02 -04:00
|
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
2022-03-07 09:12:42 -05:00
|
|
|
m_log.err("Failed to clear tray client %s '%s' (%s)", ewmh_util::get_wm_name(client.client()),
|
|
|
|
m_connection.id(client.client()), e.what());
|
2021-05-18 05:05:02 -04:00
|
|
|
}
|
2016-11-04 13:50:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_connection.flush();
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redraw window
|
|
|
|
*/
|
2022-03-06 17:43:06 -05:00
|
|
|
void tray_manager::redraw_window() {
|
2022-08-27 17:02:34 -04:00
|
|
|
m_log.info("Redraw tray container");
|
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
|
|
|
|
|
|
|
/**
|
|
|
|
* Set color atom used by clients when determing icon theme
|
|
|
|
*/
|
2016-12-05 14:41:00 -05:00
|
|
|
void tray_manager::set_tray_colors() {
|
2021-11-17 05:41:40 -05:00
|
|
|
m_log.trace("tray: Set _NET_SYSTEM_TRAY_COLORS to %x", m_opts.foreground);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2021-11-17 05:41:40 -05:00
|
|
|
auto r = m_opts.foreground.red_i();
|
|
|
|
auto g = m_opts.foreground.green_i();
|
|
|
|
auto b = m_opts.foreground.blue_i();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2021-11-17 05:41:40 -05:00
|
|
|
const uint16_t r16 = (r << 8) | r;
|
|
|
|
const uint16_t g16 = (g << 8) | g;
|
|
|
|
const uint16_t b16 = (b << 8) | b;
|
|
|
|
|
|
|
|
const uint32_t colors[12] = {
|
Add units support (POINT, PIXEL, SPACE) (#2578)
* add units support (POINT, PIXEL, SPACE) for polybar
- add a size_with_unit struct
- add a geometry_format_values struct
- move dpi initialisation from renderer.cpp to bar.cpp
- add a string to size_with_unit converter
- add point support (with pt)
- add pixel support (with px)
* Fix unit test compilation
* clang-format
* Better names
The old names didn't really capture the purpose of the structs and
function.
space_type -> spacing_type
space_size -> spacing_val
size_type -> extent_type
geometry -> extent_val
geometry_format_values -> percentage_with_offset
* Remove parse_size_with_unit
No longer needed. The convert<spacing_val> function in config.cpp
already does all the work for us and always setting the type to pixel
was wrong.
In addition, line-size should not be of type spacing_val but extent_val.
* Cleanup
I tried to address most of my comments on the old PR
* Fix renderer width calculation
We can't just blindly add the x difference to the width because for
example the width should increase if x < width and the increase keeps
x < width.
Similarly, we can't just add the offset to the width.
* Rename geom_format_to_pixels to percentage_with_offset_to_pixel
* Cleanup
* Apply suggested changes from Patrick on GitHub
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/bar.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/config.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/builder.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* Update src/components/builder.cpp
Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
* config: Use stod for parsing percentage
* Use stof instead of strtof
* units: Fix test edge cases
* Remove unnecessary clang-format toggle
* Use percentage_with_offset for margin-{top,bottom}
* Support negative extent values
* Rename unit to units and create a cpp file
* Move percentage_with_offset_to_pixel unit test to units
* Add unit tests for units_utils
* Clarify when and how negative spacing/extent is allowed
Negative spacing is never allowed and produces a config error.
Extents allow negative values in theory, but only a few use-cases accept
it.
Only the extent value used for the `%{O}` tag and the offset value in
percentage_with_offset can be negative. Everything else is capped below
at 0.
The final pixel value of percentage_with_offset also caps below at 0.
* Fix parsing errors not being caught in config
* Print a proper error message for uncaught exceptions
* Cleanup module::get_output
All changes preserve the existing semantics
* Stop using remove_trailing_space in module::get_output
Instead, we first check if the current tag is built, and only if it is,
the spacing is prepended.
* Remove unused imports
* Restore old behavior
If there are two tags and the second one isn't built (module::build
returns false), the space in between them is removed.
For example in the mpd module:
format-online = <toggle> <label-song> foo
If mpd is not running, the mpd module will return false when trying to
build the `<label-song>` tag. If we don't remove the space between
`<toggle>` and `<label-song>`, we end up with two spaces between
`<toggle>` and `foo`.
This change is to match the old behavior where at least one trailing
space character was removed from the builder.
* Add changelog entry
* Remove unused setting
* Use percentage with offset for tray-offset
Co-authored-by: Jérôme BOULMIER <jerome.boulmier@outlook.fr>
Co-authored-by: Joe Groocock <github@frebib.net>
2022-02-20 15:08:57 -05:00
|
|
|
r16, g16, b16, // normal
|
|
|
|
r16, g16, b16, // error
|
|
|
|
r16, g16, b16, // warning
|
|
|
|
r16, g16, b16, // success
|
2016-11-02 15:22:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
m_connection.change_property(
|
2022-08-27 17:02:34 -04:00
|
|
|
XCB_PROP_MODE_REPLACE, m_opts.selection_owner, _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
|
|
|
|
2022-08-27 17:02:34 -04:00
|
|
|
if (owner == m_opts.selection_owner) {
|
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 {
|
2022-08-27 17:02:34 -04:00
|
|
|
m_log.trace("tray: Change selection owner to %s", m_connection.id(m_opts.selection_owner));
|
|
|
|
m_connection.set_selection_owner_checked(m_opts.selection_owner, m_atom, XCB_CURRENT_TIME);
|
|
|
|
if (m_connection.get_selection_owner_unchecked(m_atom)->owner != m_opts.selection_owner) {
|
2016-12-21 17:22:02 -05:00
|
|
|
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());
|
2022-03-06 10:40:42 -05:00
|
|
|
message.data.data32[0] = XCB_CURRENT_TIME;
|
|
|
|
message.data.data32[1] = m_atom;
|
2022-08-27 17:02:34 -04:00
|
|
|
message.data.data32[2] = m_opts.selection_owner;
|
2016-11-25 07:55:15 -05:00
|
|
|
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");
|
2022-07-26 15:26:13 -04:00
|
|
|
const unsigned mask{XCB_CW_EVENT_MASK};
|
|
|
|
const unsigned values[]{XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
2016-11-26 00:13:20 -05:00
|
|
|
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) {
|
2021-05-18 05:02:10 -04:00
|
|
|
m_log.info("Processing docking request from '%s' (%s)", ewmh_util::get_wm_name(win), m_connection.id(win));
|
2016-11-04 13:50:33 -04:00
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
try {
|
2022-08-27 17:02:34 -04:00
|
|
|
tray_client client(m_log, m_connection, m_opts.selection_owner, win, m_opts.client_size);
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-03-16 13:37:34 -04:00
|
|
|
try {
|
|
|
|
client.query_xembed();
|
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_log.trace("tray: xembed = %s", client.is_xembed_supported() ? "true" : "false");
|
|
|
|
if (client.is_xembed_supported()) {
|
|
|
|
m_log.trace("tray: version = 0x%x, flags = 0x%x, XEMBED_MAPPED = %s", client.get_xembed().get_version(),
|
|
|
|
client.get_xembed().get_flags(), client.get_xembed().is_mapped() ? "true" : "false");
|
|
|
|
}
|
2021-09-08 12:13:21 -04:00
|
|
|
|
2022-04-04 15:06:17 -04:00
|
|
|
client.update_client_attributes();
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2022-04-04 15:06:17 -04:00
|
|
|
client.reparent();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-04-04 15:06:17 -04:00
|
|
|
client.add_to_save_set();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-04-04 15:06:17 -04:00
|
|
|
client.notify_xembed();
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-04-04 15:06:17 -04:00
|
|
|
client.ensure_state();
|
2022-03-16 13:37:34 -04:00
|
|
|
|
|
|
|
m_clients.emplace_back(std::move(client));
|
2021-05-18 05:02:10 -04:00
|
|
|
} catch (const std::exception& err) {
|
2022-03-07 06:20:05 -05:00
|
|
|
m_log.err("Failed to setup tray client '%s' (%s) removing... (%s)", ewmh_util::get_wm_name(win),
|
|
|
|
m_connection.id(win), err.what());
|
|
|
|
return;
|
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
|
|
|
|
*/
|
2022-08-27 17:02:34 -04:00
|
|
|
int tray_manager::calculate_x() const {
|
2022-08-28 08:49:09 -04:00
|
|
|
return m_bar_opts.inner_area(false).x + m_pos.x;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-02-27 14:30:23 -05:00
|
|
|
int tray_manager::calculate_y() const {
|
2022-08-28 08:49:09 -04:00
|
|
|
return m_bar_opts.inner_area(false).y + m_pos.y;
|
2016-12-03 14:26:29 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2022-07-26 15:26:13 -04:00
|
|
|
unsigned tray_manager::calculate_w() const {
|
|
|
|
unsigned width = m_opts.spacing;
|
|
|
|
unsigned count{0};
|
2022-03-07 06:20:05 -05:00
|
|
|
for (auto& client : m_clients) {
|
|
|
|
if (client.mapped()) {
|
2016-12-05 14:41:00 -05:00
|
|
|
count++;
|
2022-02-27 15:36:16 -05:00
|
|
|
width += m_opts.spacing + m_opts.client_size.w;
|
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 y position of client window
|
|
|
|
*/
|
2017-01-19 05:11:28 -05:00
|
|
|
int tray_manager::calculate_client_y() {
|
2022-08-28 08:49:09 -04:00
|
|
|
return (m_bar_opts.inner_area(true).height - m_opts.client_size.h) / 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
|
|
|
/**
|
2022-09-03 16:34:00 -04:00
|
|
|
* Check if the given window is embedded.
|
|
|
|
*
|
|
|
|
* The given window ID can be the ID of the wrapper or the embedded window
|
2016-12-05 14:41:00 -05:00
|
|
|
*/
|
2022-09-03 16:34:00 -04:00
|
|
|
bool tray_manager::is_embedded(const xcb_window_t& win) {
|
|
|
|
return find_client(win) != nullptr;
|
2016-12-05 14:41:00 -05:00
|
|
|
}
|
|
|
|
|
2016-11-02 15:22:45 -04:00
|
|
|
/**
|
2022-09-03 16:34:00 -04:00
|
|
|
* Find tray client object from the wrapper or embedded window
|
2016-11-02 15:22:45 -04:00
|
|
|
*/
|
2022-03-07 06:20:05 -05:00
|
|
|
tray_client* tray_manager::find_client(const xcb_window_t& win) {
|
2022-09-03 16:34:00 -04:00
|
|
|
auto client = std::find_if(m_clients.begin(), m_clients.end(),
|
|
|
|
[win](const auto& client) { return client.match(win) || client.embedder() == win; });
|
|
|
|
|
|
|
|
if (client == m_clients.end()) {
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
return &(*client);
|
2016-11-25 07:55:15 -05: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
|
|
|
* Remove tray client
|
2016-11-02 15:22:45 -04:00
|
|
|
*/
|
2022-03-07 06:20:05 -05:00
|
|
|
void tray_manager::remove_client(const tray_client& client, bool reconfigure) {
|
2022-03-07 09:12:42 -05:00
|
|
|
remove_client(client.client(), reconfigure);
|
2016-12-05 14:41:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove tray client by window
|
|
|
|
*/
|
|
|
|
void tray_manager::remove_client(xcb_window_t win, bool reconfigure) {
|
2022-03-07 06:20:05 -05:00
|
|
|
m_clients.erase(
|
|
|
|
std::remove_if(m_clients.begin(), m_clients.end(), [win](const auto& 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
|
|
|
|
2022-06-16 06:53:49 -04:00
|
|
|
bool tray_manager::change_visibility(bool visible) {
|
2022-08-27 17:02:34 -04:00
|
|
|
if (!m_activated || m_hidden == !visible) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-06-16 06:53:49 -04:00
|
|
|
|
2022-08-27 17:02:34 -04:00
|
|
|
m_log.trace("tray: visibility_change (state=%i, activated=%i, hidden=%i)", visible, static_cast<bool>(m_activated),
|
|
|
|
static_cast<bool>(m_hidden));
|
2022-06-16 06:53:49 -04:00
|
|
|
|
|
|
|
m_hidden = !visible;
|
|
|
|
|
2022-08-27 17:02:34 -04:00
|
|
|
for (auto& client : m_clients) {
|
|
|
|
client.hidden(m_hidden);
|
|
|
|
client.ensure_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_hidden) {
|
2022-06-16 06:53:49 -04:00
|
|
|
redraw_window();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_connection.flush();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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;
|
2022-08-27 17:02:34 -04:00
|
|
|
} else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW &&
|
|
|
|
evt->window == m_opts.selection_owner) {
|
2020-04-21 18:14:02 -04:00
|
|
|
m_log.notice("Received WM_DELETE");
|
2016-12-05 14:41:00 -05:00
|
|
|
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]) {
|
2022-02-27 14:30:23 -05:00
|
|
|
xcb_window_t win = evt->data.data32[2];
|
|
|
|
if (!is_embedded(win)) {
|
|
|
|
process_docking_request(win);
|
2016-12-05 14:41:00 -05:00
|
|
|
} else {
|
|
|
|
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));
|
2022-04-04 15:06:17 -04:00
|
|
|
find_client(evt->window)->configure_notify();
|
2016-12-05 14:41:00 -05:00
|
|
|
} 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
|
|
|
|
|
|
|
/**
|
2022-02-20 15:40:48 -05:00
|
|
|
* @see tray_manager::handle(const evt::configure_request&);
|
2016-11-02 15:22:45 -04:00
|
|
|
*/
|
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));
|
2022-04-04 15:06:17 -04:00
|
|
|
find_client(evt->window)->configure_notify();
|
2016-12-05 14:41:00 -05:00
|
|
|
} 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;
|
2022-08-27 17:02:34 -04:00
|
|
|
} else if (evt->owner != m_opts.selection_owner) {
|
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;
|
2022-04-15 17:50:04 -04:00
|
|
|
}
|
|
|
|
|
2022-03-07 13:16:07 -05:00
|
|
|
if (evt->atom != _XEMBED_INFO) {
|
2016-12-05 14:41:00 -05:00
|
|
|
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
|
|
|
if (evt->state == XCB_PROPERTY_NEW_VALUE) {
|
|
|
|
m_log.trace("tray: _XEMBED_INFO value has changed");
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2017-03-15 18:52:59 -04:00
|
|
|
try {
|
2021-09-08 12:13:21 -04:00
|
|
|
client->query_xembed();
|
2017-03-15 18:52:59 -04:00
|
|
|
} catch (const xpp::x::error::window& err) {
|
|
|
|
m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what());
|
2022-03-07 09:12:42 -05:00
|
|
|
remove_client(*client, true);
|
2017-03-15 18:52:59 -04:00
|
|
|
return;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2021-09-08 12:13:21 -04:00
|
|
|
m_log.trace("tray: version = 0x%x, flags = 0x%x, XEMBED_MAPPED = %s", client->get_xembed().get_version(),
|
|
|
|
client->get_xembed().get_flags(), client->get_xembed().is_mapped() ? "true" : "false");
|
2016-12-05 14:41:00 -05:00
|
|
|
|
2021-09-08 12:13:21 -04:00
|
|
|
if (client->get_xembed().is_mapped()) {
|
2016-12-05 14:41:00 -05:00
|
|
|
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) {
|
2022-03-07 09:12:42 -05:00
|
|
|
if (!m_activated) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto client = find_client(evt->window);
|
|
|
|
|
|
|
|
if (!client) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (evt->parent != client->embedder()) {
|
2022-09-03 16:34:00 -04:00
|
|
|
m_log.info("tray: Received reparent_notify for client, remove...");
|
2022-03-07 09:12:42 -05:00
|
|
|
remove_client(*client);
|
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) {
|
2022-08-27 17:02:34 -04:00
|
|
|
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)) {
|
2022-09-03 16:34:00 -04:00
|
|
|
m_log.info("tray: Received destroy_notify for client, remove...");
|
2016-12-05 14:41:00 -05:00
|
|
|
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) {
|
2022-08-27 17:02:34 -04:00
|
|
|
if (m_activated && evt->window == m_opts.selection_owner) {
|
2016-12-03 14:26:29 -05:00
|
|
|
m_log.trace("tray: Received map_notify");
|
|
|
|
m_log.trace("tray: Update container mapped flag");
|
2016-12-05 14:41:00 -05:00
|
|
|
redraw_window();
|
2022-09-03 16:34:00 -04:00
|
|
|
} else if (is_embedded(evt->window)) {
|
2016-12-05 14:41:00 -05:00
|
|
|
m_log.trace("tray: Received map_notify");
|
|
|
|
m_log.trace("tray: Set client mapped");
|
2022-08-28 08:49:09 -04:00
|
|
|
auto client = find_client(evt->window);
|
|
|
|
|
|
|
|
if (!client->mapped()) {
|
|
|
|
client->mapped(true);
|
2016-12-03 14:26:29 -05:00
|
|
|
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_UNMAP_NOTIFY
|
|
|
|
*/
|
2016-12-03 14:26:29 -05:00
|
|
|
void tray_manager::handle(const evt::unmap_notify& evt) {
|
2022-09-03 16:34:00 -04:00
|
|
|
if (m_activated && is_embedded(evt->window)) {
|
2016-12-05 14:41:00 -05:00
|
|
|
m_log.trace("tray: Received unmap_notify");
|
|
|
|
m_log.trace("tray: Set client unmapped");
|
2022-08-28 08:49:09 -04:00
|
|
|
auto client = find_client(evt->window);
|
|
|
|
|
|
|
|
if (client->mapped()) {
|
|
|
|
client->mapped(false);
|
|
|
|
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
|
|
|
|
2022-04-15 18:20:00 -04:00
|
|
|
// TODO maybe remove signal
|
2017-05-23 15:32:34 -04:00
|
|
|
bool tray_manager::on(const signals::ui::update_background&) {
|
2022-03-06 17:43:06 -05:00
|
|
|
redraw_window();
|
2017-05-23 15:32:34 -04:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-15 17:50:04 -04:00
|
|
|
bool tray_manager::on(const signals::ui_tray::tray_pos_change& evt) {
|
2022-08-28 08:49:09 -04:00
|
|
|
int new_x = std::max(0, std::min(evt.cast(), (int)(m_bar_opts.size.w - m_tray_width)));
|
2022-06-14 09:35:49 -04:00
|
|
|
|
2022-08-28 08:49:09 -04:00
|
|
|
if (new_x != m_pos.x) {
|
|
|
|
m_pos.x = new_x;
|
|
|
|
reconfigure();
|
|
|
|
}
|
2022-04-15 17:50:04 -04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-16 06:53:49 -04:00
|
|
|
bool tray_manager::on(const signals::ui_tray::tray_visibility& evt) {
|
2022-08-27 17:02:34 -04:00
|
|
|
return change_visibility(evt.cast());
|
2022-06-16 06:53:49 -04:00
|
|
|
}
|
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS_END
|