Merge tag '3.5.7'

This commit is contained in:
Przemek Grondek 2023-08-05 17:26:56 +02:00
commit 771cd1fd98
12 changed files with 229 additions and 111 deletions

View File

@ -1,4 +1,5 @@
# Changelog
All notable changes to this project will be documented in this file.
Each release should have the following subsections, if entries exist, in the
given order: `Breaking`, `Build`, `Deprecated`, `Removed`, `Added`, `Changed`,
@ -56,3 +57,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([`#2040`](https://github.com/polybar/polybar/issues/2040))
[Unreleased]: https://github.com/polybar/polybar/compare/3.5.2...HEAD
## [3.5.7] - 2021-09-21
### Fixed
- The tray mistakenly removed tray icons that did not support XEMBED
([`#2479`](https://github.com/polybar/polybar/issues/2479),
[`#2442`](https://github.com/polybar/polybar/issues/2442))
- `custom/ipc`: Only the first appearance of the `%pid%` token was replaced
([`#2500`](https://github.com/polybar/polybar/issues/2500))
## [3.5.6] - 2021-05-24
### Build
- Support building documentation on sphinx 4.0 ([`#2424`](https://github.com/polybar/polybar/issues/2424))
### Fixed
- Tray icons sometimes appears outside of bar ([`#2430`](https://github.com/polybar/polybar/issues/2430), [`#1679`](https://github.com/polybar/polybar/issues/1679))
- Crash in the i3 module ([`#2416`](https://github.com/polybar/polybar/issues/2416))
## [3.5.5] - 2021-03-01
### Build
- Support older python sphinx versions again ([`#2356`](https://github.com/polybar/polybar/issues/2356))
## [3.5.4] - 2021-01-07
### Fixed
- Wrong text displayed if module text ends with `}` ([`#2331`](https://github.com/polybar/polybar/issues/2331))
## [3.5.3] - 2020-12-23
### Build
- Don't use `git` when building documentation ([`#2309`](https://github.com/polybar/polybar/issues/2309))
### Fixed
- Empty color values are no longer treated as invalid and no longer produce an error.
[Unreleased]: https://github.com/polybar/polybar/compare/3.5.7...HEAD
[3.5.7]: https://github.com/polybar/polybar/releases/tag/3.5.7
[3.5.6]: https://github.com/polybar/polybar/releases/tag/3.5.6
[3.5.5]: https://github.com/polybar/polybar/releases/tag/3.5.5
[3.5.4]: https://github.com/polybar/polybar/releases/tag/3.5.4
[3.5.3]: https://github.com/polybar/polybar/releases/tag/3.5.3

View File

@ -13,12 +13,22 @@
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
from pathlib import Path
import datetime
import subprocess
from docutils.nodes import Node
from typing import List
from sphinx.domains.changeset import VersionChange
import sphinx
import packaging.version
def get_version(root_path):
"""
Reads the polybar version from the version.txt at the root of the repo.
"""
path = Path(root_path) / "version.txt"
with open(path, "r") as f:
for line in f.readlines():
if not line.startswith("#"):
return packaging.version.parse(line)
raise RuntimeError("No version found in {}".format(path))
# -- Project information -----------------------------------------------------
@ -50,6 +60,11 @@ else:
# build folder.
doc_path = '@doc_path@'
# The version from the version.txt file. Since we are not always first
# configured by cmake, we don't necessarily have access to the current version
# number
version_txt = get_version(Path(doc_path).absolute().parent)
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@ -170,6 +185,9 @@ man_pages = [
('man/polybar.5', 'polybar', 'configuration file for polybar(1)', [], 5)
]
man_make_section_directory = False
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
@ -202,23 +220,32 @@ epub_exclude_files = ['search.html']
# The 'versionadded' and 'versionchanged' directives are overridden.
suppress_warnings = ['app.add_directive']
def setup(app):
# It is not exactly clear in which version the VersionChange class was
# introduced, but we know it is available in at least 1.8.5.
# This feature is mainly needed for the online docs on readthedocs for the docs
# built from master, documentation built for proper releases should not even
# mention unreleased changes. Because of that it's not that important that this
# is added to local builds.
if packaging.version.parse(sphinx.__version__) >= packaging.version.parse("1.8.5"):
from typing import List
from docutils.nodes import Node
from sphinx.domains.changeset import VersionChange
def setup(app):
app.add_directive('deprecated', VersionDirective)
app.add_directive('versionadded', VersionDirective)
app.add_directive('versionchanged', VersionDirective)
class VersionDirective(VersionChange):
class VersionDirective(VersionChange):
"""
Overwrites the Sphinx directive for versionchanged, versionadded, and
deprecated and adds an unreleased tag to versions that are not yet released
"""
def run(self) -> List[Node]:
# If the tag exists 'git rev-parse' will succeed and otherwise fail
completed = subprocess.run(["git", "rev-parse", self.arguments[0]],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=doc_path,
check=False)
directive_version = packaging.version.parse(self.arguments[0])
if completed.returncode != 0:
if directive_version > version_txt:
self.arguments[0] += " (unreleased)"
return super().run()

View File

@ -4,12 +4,12 @@
#include "common.hpp"
#include "utils/concurrency.hpp"
#include "x11/xembed.hpp"
POLYBAR_NS
// fwd declarations
class connection;
struct xembed_data;
class tray_client {
public:
@ -28,7 +28,10 @@ class tray_client {
void mapped(bool state);
xcb_window_t window() const;
xembed_data* xembed() const;
void query_xembed();
bool is_xembed_supported() const;
const xembed::info& get_xembed() const;
void ensure_state() const;
void reconfigure(int x, int y) const;
@ -38,7 +41,18 @@ class tray_client {
connection& m_connection;
xcb_window_t m_window{0};
shared_ptr<xembed_data> m_xembed;
/**
* Whether the client window supports XEMBED.
*
* A tray client can still work when it doesn't support XEMBED.
*/
bool m_xembed_supported{false};
/**
* _XEMBED_INFO of the client window
*/
xembed::info m_xembed;
bool m_mapped{false};
unsigned int m_width;

View File

@ -34,7 +34,6 @@ using namespace std::chrono_literals;
// fwd declarations
class connection;
struct xembed_data;
class background_manager;
class bg_slice;

View File

@ -21,24 +21,41 @@ POLYBAR_NS
#define XEMBED_FOCUS_FIRST 1
#define XEMBED_FOCUS_LAST 1
struct xembed_data {
unsigned long version;
unsigned long flags;
xcb_timestamp_t time;
xcb_atom_t xembed;
xcb_atom_t xembed_info;
};
/**
* Max XEMBED version supported.
*/
#define XEMBED_MAX_VERSION UINT32_C(0)
/**
* Implementation of parts of the XEMBED spec (as much as needed to get the tray working).
*
* Ref: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
*/
namespace xembed {
xembed_data* query(connection& conn, xcb_window_t win, xembed_data* data);
class info {
public:
void set(uint32_t* data);
uint32_t get_version() const;
uint32_t get_flags() const;
bool is_mapped() const;
protected:
uint32_t version;
uint32_t flags;
};
bool query(connection& conn, xcb_window_t win, info& data);
void send_message(connection& conn, xcb_window_t target, long message, long d1, long d2, long d3);
void send_focus_event(connection& conn, xcb_window_t target);
void notify_embedded(connection& conn, xcb_window_t win, xcb_window_t embedder, long version);
void notify_embedded(connection& conn, xcb_window_t win, xcb_window_t embedder, uint32_t version);
void notify_activated(connection& conn, xcb_window_t win);
void notify_deactivated(connection& conn, xcb_window_t win);
void notify_focused(connection& conn, xcb_window_t win, long focus_type);
void notify_unfocused(connection& conn, xcb_window_t win);
void unembed(connection& conn, xcb_window_t win, xcb_window_t root);
}
} // namespace xembed
POLYBAR_NS_END

View File

@ -225,6 +225,10 @@ chrono::duration<double> config::convert(string&& value) const {
template <>
rgba config::convert(string&& value) const {
if (value.empty()) {
return rgba{};
}
rgba ret{value};
if (!ret.has_color()) {

View File

@ -655,16 +655,7 @@ bool controller::process_update(bool force) {
block_contents += padding_right;
}
// Strip unnecessary reset tags
block_contents = string_util::replace_all(block_contents, "T-}%{T", "T");
block_contents = string_util::replace_all(block_contents, "B-}%{B#", "B#");
block_contents = string_util::replace_all(block_contents, "F-}%{F#", "F#");
block_contents = string_util::replace_all(block_contents, "U-}%{U#", "U#");
block_contents = string_util::replace_all(block_contents, "u-}%{u#", "u#");
block_contents = string_util::replace_all(block_contents, "o-}%{o#", "o#");
// Join consecutive tags
contents += string_util::replace_all(block_contents, "}%{", " ");
contents += block_contents;
}
try {

View File

@ -38,18 +38,15 @@ namespace modules {
m_actions.emplace(make_pair<mousebtn, string>(mousebtn::DOUBLE_RIGHT, m_conf.get(name(), "double-click-right", ""s)));
// clang-format on
const auto pid_token = [](string& s) {
string::size_type p = s.find("%pid%");
if (p != string::npos) {
s.replace(p, 5, to_string(getpid()));
}
const auto pid_token = [](const string& s) {
return string_util::replace_all(s, "%pid%", to_string(getpid()));
};
for (auto& action : m_actions) {
pid_token(action.second);
action.second = pid_token(action.second);
}
for (auto& hook : m_hooks) {
pid_token(hook->command);
hook->command = pid_token(hook->command);
}
m_formatter->add(DEFAULT_FORMAT, TAG_OUTPUT, {TAG_OUTPUT});

View File

@ -1,19 +1,15 @@
#include "x11/tray_client.hpp"
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include "utils/memory.hpp"
#include "x11/connection.hpp"
#include "x11/tray_client.hpp"
#include "x11/xembed.hpp"
POLYBAR_NS
tray_client::tray_client(connection& conn, xcb_window_t win, unsigned int w, unsigned int h)
: m_connection(conn), m_window(win), m_width(w), m_height(h) {
m_xembed = memory_util::make_malloc_ptr<xembed_data>();
m_xembed->version = XEMBED_VERSION;
m_xembed->flags = XEMBED_MAPPED;
}
: m_connection(conn), m_window(win), m_width(w), m_height(h) {}
tray_client::~tray_client() {
xembed::unembed(m_connection, window(), m_connection.root());
@ -28,11 +24,7 @@ unsigned int tray_client::height() const {
}
void tray_client::clear_window() const {
try {
m_connection.clear_area_checked(1, window(), 0, 0, width(), height());
} catch (const xpp::x::error::window& err) {
// ignore
}
}
/**
@ -63,20 +55,31 @@ xcb_window_t tray_client::window() const {
return m_window;
}
/**
* Get xembed data pointer
*/
xembed_data* tray_client::xembed() const {
return m_xembed.get();
void tray_client::query_xembed() {
m_xembed_supported = xembed::query(m_connection, m_window, m_xembed);
}
bool tray_client::is_xembed_supported() const {
return m_xembed_supported;
}
const xembed::info& tray_client::get_xembed() const {
return m_xembed;
}
/**
* Make sure that the window mapping state is correct
*/
void tray_client::ensure_state() const {
if (!mapped() && ((xembed()->flags & XEMBED_MAPPED) == XEMBED_MAPPED)) {
bool should_be_mapped = true;
if (is_xembed_supported()) {
should_be_mapped = m_xembed.is_mapped();
}
if (!mapped() && should_be_mapped) {
m_connection.map_window_checked(window());
} else if (mapped() && ((xembed()->flags & XEMBED_MAPPED) != XEMBED_MAPPED)) {
} else if (mapped() && !should_be_mapped) {
m_connection.unmap_window_checked(window());
}
}

View File

@ -21,6 +21,12 @@
#include "x11/winspec.hpp"
#include "x11/xembed.hpp"
/*
* Tray implementation according to the System Tray Protocol.
*
* Ref: https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
*/
// ====================================================================================================
//
// TODO: 32-bit visual
@ -427,14 +433,22 @@ void tray_manager::refresh_window() {
m_connection.poly_fill_rectangle(m_pixmap, m_gc, 1, &rect);
}
if (m_surface)
if (m_surface) {
m_surface->flush();
}
m_connection.clear_area(0, m_tray, 0, 0, width, height);
for (auto&& client : m_clients) {
try {
if (client->mapped()) {
client->clear_window();
}
} catch (const std::exception& e) {
m_log.err("Failed to clear tray client %s '%s' (%s)", m_connection.id(client->window()),
ewmh_util::get_wm_name(client->window()), e.what());
}
}
m_connection.flush();
@ -726,26 +740,29 @@ void tray_manager::track_selection_owner(xcb_window_t owner) {
* Process client docking request
*/
void tray_manager::process_docking_request(xcb_window_t win) {
m_log.info("Processing docking request from %s", m_connection.id(win));
m_log.info("Processing docking request from '%s' (%s)", ewmh_util::get_wm_name(win), m_connection.id(win));
m_clients.emplace_back(factory_util::shared<tray_client>(m_connection, win, m_opts.width, m_opts.height));
auto& client = m_clients.back();
try {
m_log.trace("tray: Get client _XEMBED_INFO");
xembed::query(m_connection, win, client->xembed());
} catch (const application_error& err) {
m_log.err(err.what());
client->query_xembed();
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what());
remove_client(win, true);
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");
}
try {
const unsigned int mask{XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK};
const unsigned int values[]{
XCB_BACK_PIXMAP_PARENT_RELATIVE, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
const unsigned int mask = XCB_CW_EVENT_MASK;
const unsigned int values[]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_log.trace("tray: Update client window");
m_connection.change_window_attributes_checked(client->window(), mask, values);
@ -760,15 +777,18 @@ void tray_manager::process_docking_request(xcb_window_t win) {
m_connection.reparent_window_checked(
client->window(), m_tray, calculate_client_x(client->window()), calculate_client_y());
if (client->is_xembed_supported()) {
m_log.trace("tray: Send embbeded notification to client");
xembed::notify_embedded(m_connection, client->window(), m_tray, client->xembed()->version);
xembed::notify_embedded(m_connection, client->window(), m_tray, client->get_xembed().get_version());
}
if (client->xembed()->flags & XEMBED_MAPPED) {
if (!client->is_xembed_supported() || client->get_xembed().is_mapped()) {
m_log.trace("tray: Map client");
m_connection.map_window_checked(client->window());
}
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to setup tray client, removing... (%s)", err.what());
} catch (const std::exception& err) {
m_log.err("Failed to setup tray client removing... (%s)", err.what());
remove_client(win, false);
}
}
@ -1013,7 +1033,6 @@ void tray_manager::handle(const evt::property_notify& evt) {
m_log.trace("tray: _XEMBED_INFO: %s", m_connection.id(evt->window));
auto xd = client->xembed();
auto win = client->window();
if (evt->state == XCB_PROPERTY_NEW_VALUE) {
@ -1021,20 +1040,17 @@ void tray_manager::handle(const evt::property_notify& evt) {
}
try {
m_log.trace("tray: Get client _XEMBED_INFO");
xembed::query(m_connection, win, xd);
} catch (const application_error& err) {
m_log.err(err.what());
return;
client->query_xembed();
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what());
remove_client(win, true);
return;
}
m_log.trace("tray: _XEMBED_INFO[0]=%u _XEMBED_INFO[1]=%u", xd->version, xd->flags);
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");
if ((client->xembed()->flags & XEMBED_MAPPED) & XEMBED_MAPPED) {
if (client->get_xembed().is_mapped()) {
reconfigure();
}
}

View File

@ -5,26 +5,40 @@
POLYBAR_NS
namespace xembed {
/**
* Query _XEMBED_INFO for the given window
*/
xembed_data* query(connection& conn, xcb_window_t win, xembed_data* data) {
auto info = conn.get_property(false, win, _XEMBED_INFO, XCB_GET_PROPERTY_TYPE_ANY, 0L, 2);
if (info->value_len == 0) {
throw application_error("Invalid _XEMBED_INFO for window " + conn.id(win));
void info::set(uint32_t* data) {
version = data[0];
flags = data[1];
}
std::vector<unsigned int> xembed_data{info.value<unsigned int>().begin(), info.value<unsigned int>().end()};
uint32_t info::get_version() const {
return version;
}
data->xembed = _XEMBED;
data->xembed_info = _XEMBED_INFO;
uint32_t info::get_flags() const {
return flags;
}
data->time = XCB_CURRENT_TIME;
data->flags = xembed_data[1];
data->version = xembed_data[0];
bool info::is_mapped() const {
return (flags & XEMBED_MAPPED) == XEMBED_MAPPED;
}
return data;
/**
* Query _XEMBED_INFO for the given window
*
* \return Whether valid XEMBED info was found
*/
bool query(connection& conn, xcb_window_t win, info& data) {
auto info = conn.get_property(false, win, _XEMBED_INFO, _XEMBED_INFO, 0L, 2);
if (info->value_len == 0) {
return false;
}
std::vector<uint32_t> xembed_data{info.value<uint32_t>().begin(), info.value<uint32_t>().end()};
data.set(xembed_data.data());
return true;
}
/**
@ -53,8 +67,8 @@ namespace xembed {
/**
* Acknowledge window embedding
*/
void notify_embedded(connection& conn, xcb_window_t win, xcb_window_t embedder, long version) {
send_message(conn, win, XEMBED_EMBEDDED_NOTIFY, 0, embedder, version);
void notify_embedded(connection& conn, xcb_window_t win, xcb_window_t embedder, uint32_t version) {
send_message(conn, win, XEMBED_EMBEDDED_NOTIFY, 0, embedder, std::min(version, XEMBED_MAX_VERSION));
}
/**

View File

@ -1,4 +1,4 @@
# Polybar version information
# Update this on every release
# This is used to create the version string if a git repo is not available
3.5.2
3.5.7