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 # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
Each release should have the following subsections, if entries exist, in the Each release should have the following subsections, if entries exist, in the
given order: `Breaking`, `Build`, `Deprecated`, `Removed`, `Added`, `Changed`, 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)) ([`#2040`](https://github.com/polybar/polybar/issues/2040))
[Unreleased]: https://github.com/polybar/polybar/compare/3.5.2...HEAD [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. # documentation root, use os.path.abspath to make it absolute, like shown here.
# #
import os import os
from pathlib import Path
import datetime import datetime
import subprocess import sphinx
from docutils.nodes import Node import packaging.version
from typing import List
from sphinx.domains.changeset import VersionChange
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 ----------------------------------------------------- # -- Project information -----------------------------------------------------
@ -50,6 +60,11 @@ else:
# build folder. # build folder.
doc_path = '@doc_path@' 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 --------------------------------------------------- # -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # 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/polybar.5', 'polybar', 'configuration file for polybar(1)', [], 5)
] ]
man_make_section_directory = False
# -- Options for Texinfo output ---------------------------------------------- # -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples # Grouping the document tree into Texinfo files. List of tuples
@ -202,6 +220,18 @@ epub_exclude_files = ['search.html']
# The 'versionadded' and 'versionchanged' directives are overridden. # The 'versionadded' and 'versionchanged' directives are overridden.
suppress_warnings = ['app.add_directive'] suppress_warnings = ['app.add_directive']
# 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): def setup(app):
app.add_directive('deprecated', VersionDirective) app.add_directive('deprecated', VersionDirective)
app.add_directive('versionadded', VersionDirective) app.add_directive('versionadded', VersionDirective)
@ -213,12 +243,9 @@ class VersionDirective(VersionChange):
deprecated and adds an unreleased tag to versions that are not yet released deprecated and adds an unreleased tag to versions that are not yet released
""" """
def run(self) -> List[Node]: def run(self) -> List[Node]:
# If the tag exists 'git rev-parse' will succeed and otherwise fail directive_version = packaging.version.parse(self.arguments[0])
completed = subprocess.run(["git", "rev-parse", self.arguments[0]],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=doc_path,
check=False)
if completed.returncode != 0: if directive_version > version_txt:
self.arguments[0] += " (unreleased)" self.arguments[0] += " (unreleased)"
return super().run() return super().run()

View File

@ -4,12 +4,12 @@
#include "common.hpp" #include "common.hpp"
#include "utils/concurrency.hpp" #include "utils/concurrency.hpp"
#include "x11/xembed.hpp"
POLYBAR_NS POLYBAR_NS
// fwd declarations // fwd declarations
class connection; class connection;
struct xembed_data;
class tray_client { class tray_client {
public: public:
@ -28,7 +28,10 @@ class tray_client {
void mapped(bool state); void mapped(bool state);
xcb_window_t window() const; 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 ensure_state() const;
void reconfigure(int x, int y) const; void reconfigure(int x, int y) const;
@ -38,7 +41,18 @@ class tray_client {
connection& m_connection; connection& m_connection;
xcb_window_t m_window{0}; 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}; bool m_mapped{false};
unsigned int m_width; unsigned int m_width;

View File

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

View File

@ -21,24 +21,41 @@ POLYBAR_NS
#define XEMBED_FOCUS_FIRST 1 #define XEMBED_FOCUS_FIRST 1
#define XEMBED_FOCUS_LAST 1 #define XEMBED_FOCUS_LAST 1
struct xembed_data { /**
unsigned long version; * Max XEMBED version supported.
unsigned long flags; */
xcb_timestamp_t time; #define XEMBED_MAX_VERSION UINT32_C(0)
xcb_atom_t xembed;
xcb_atom_t xembed_info; /**
* 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 {
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;
}; };
namespace xembed { bool query(connection& conn, xcb_window_t win, info& data);
xembed_data* query(connection& conn, xcb_window_t win, xembed_data* data);
void send_message(connection& conn, xcb_window_t target, long message, long d1, long d2, long d3); 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 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_activated(connection& conn, xcb_window_t win);
void notify_deactivated(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_focused(connection& conn, xcb_window_t win, long focus_type);
void notify_unfocused(connection& conn, xcb_window_t win); void notify_unfocused(connection& conn, xcb_window_t win);
void unembed(connection& conn, xcb_window_t win, xcb_window_t root); void unembed(connection& conn, xcb_window_t win, xcb_window_t root);
} } // namespace xembed
POLYBAR_NS_END POLYBAR_NS_END

View File

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

View File

@ -655,16 +655,7 @@ bool controller::process_update(bool force) {
block_contents += padding_right; block_contents += padding_right;
} }
// Strip unnecessary reset tags contents += block_contents;
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, "}%{", " ");
} }
try { 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))); m_actions.emplace(make_pair<mousebtn, string>(mousebtn::DOUBLE_RIGHT, m_conf.get(name(), "double-click-right", ""s)));
// clang-format on // clang-format on
const auto pid_token = [](string& s) { const auto pid_token = [](const string& s) {
string::size_type p = s.find("%pid%"); return string_util::replace_all(s, "%pid%", to_string(getpid()));
if (p != string::npos) {
s.replace(p, 5, to_string(getpid()));
}
}; };
for (auto& action : m_actions) { for (auto& action : m_actions) {
pid_token(action.second); action.second = pid_token(action.second);
} }
for (auto& hook : m_hooks) { for (auto& hook : m_hooks) {
pid_token(hook->command); hook->command = pid_token(hook->command);
} }
m_formatter->add(DEFAULT_FORMAT, TAG_OUTPUT, {TAG_OUTPUT}); 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.h>
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
#include "utils/memory.hpp" #include "utils/memory.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/tray_client.hpp"
#include "x11/xembed.hpp"
POLYBAR_NS POLYBAR_NS
tray_client::tray_client(connection& conn, xcb_window_t win, unsigned int w, unsigned int h) 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_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;
}
tray_client::~tray_client() { tray_client::~tray_client() {
xembed::unembed(m_connection, window(), m_connection.root()); xembed::unembed(m_connection, window(), m_connection.root());
@ -28,11 +24,7 @@ unsigned int tray_client::height() const {
} }
void tray_client::clear_window() const { void tray_client::clear_window() const {
try {
m_connection.clear_area_checked(1, window(), 0, 0, width(), height()); 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; return m_window;
} }
/** void tray_client::query_xembed() {
* Get xembed data pointer m_xembed_supported = xembed::query(m_connection, m_window, m_xembed);
*/ }
xembed_data* tray_client::xembed() const {
return m_xembed.get(); 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 * Make sure that the window mapping state is correct
*/ */
void tray_client::ensure_state() const { 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()); 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()); m_connection.unmap_window_checked(window());
} }
} }

View File

@ -21,6 +21,12 @@
#include "x11/winspec.hpp" #include "x11/winspec.hpp"
#include "x11/xembed.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 // TODO: 32-bit visual
@ -427,14 +433,22 @@ void tray_manager::refresh_window() {
m_connection.poly_fill_rectangle(m_pixmap, m_gc, 1, &rect); m_connection.poly_fill_rectangle(m_pixmap, m_gc, 1, &rect);
} }
if (m_surface) if (m_surface) {
m_surface->flush(); m_surface->flush();
}
m_connection.clear_area(0, m_tray, 0, 0, width, height); m_connection.clear_area(0, m_tray, 0, 0, width, height);
for (auto&& client : m_clients) { for (auto&& client : m_clients) {
try {
if (client->mapped()) {
client->clear_window(); 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(); m_connection.flush();
@ -726,26 +740,29 @@ void tray_manager::track_selection_owner(xcb_window_t owner) {
* Process client docking request * Process client docking request
*/ */
void tray_manager::process_docking_request(xcb_window_t win) { 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)); m_clients.emplace_back(factory_util::shared<tray_client>(m_connection, win, m_opts.width, m_opts.height));
auto& client = m_clients.back(); auto& client = m_clients.back();
try { try {
m_log.trace("tray: Get client _XEMBED_INFO"); client->query_xembed();
xembed::query(m_connection, win, client->xembed());
} catch (const application_error& err) {
m_log.err(err.what());
} catch (const xpp::x::error::window& err) { } catch (const xpp::x::error::window& err) {
m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what()); m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what());
remove_client(win, true); remove_client(win, true);
return; 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 { try {
const unsigned int mask{XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK}; const unsigned int mask = XCB_CW_EVENT_MASK;
const unsigned int values[]{ const unsigned int values[]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
XCB_BACK_PIXMAP_PARENT_RELATIVE, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_log.trace("tray: Update client window"); m_log.trace("tray: Update client window");
m_connection.change_window_attributes_checked(client->window(), mask, values); 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( m_connection.reparent_window_checked(
client->window(), m_tray, calculate_client_x(client->window()), calculate_client_y()); 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"); 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_log.trace("tray: Map client");
m_connection.map_window_checked(client->window()); 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); 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)); m_log.trace("tray: _XEMBED_INFO: %s", m_connection.id(evt->window));
auto xd = client->xembed();
auto win = client->window(); auto win = client->window();
if (evt->state == XCB_PROPERTY_NEW_VALUE) { if (evt->state == XCB_PROPERTY_NEW_VALUE) {
@ -1021,20 +1040,17 @@ void tray_manager::handle(const evt::property_notify& evt) {
} }
try { try {
m_log.trace("tray: Get client _XEMBED_INFO"); client->query_xembed();
xembed::query(m_connection, win, xd);
} catch (const application_error& err) {
m_log.err(err.what());
return;
} catch (const xpp::x::error::window& err) { } catch (const xpp::x::error::window& err) {
m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what()); m_log.err("Failed to query _XEMBED_INFO, removing client... (%s)", err.what());
remove_client(win, true); remove_client(win, true);
return; 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(); reconfigure();
} }
} }

View File

@ -5,26 +5,40 @@
POLYBAR_NS POLYBAR_NS
namespace xembed { 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) { void info::set(uint32_t* data) {
throw application_error("Invalid _XEMBED_INFO for window " + conn.id(win)); 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; uint32_t info::get_flags() const {
data->xembed_info = _XEMBED_INFO; return flags;
}
data->time = XCB_CURRENT_TIME; bool info::is_mapped() const {
data->flags = xembed_data[1]; return (flags & XEMBED_MAPPED) == XEMBED_MAPPED;
data->version = xembed_data[0]; }
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 * Acknowledge window embedding
*/ */
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) {
send_message(conn, win, XEMBED_EMBEDDED_NOTIFY, 0, embedder, 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 # Polybar version information
# Update this on every release # Update this on every release
# This is used to create the version string if a git repo is not available # This is used to create the version string if a git repo is not available
3.5.2 3.5.7