diff --git a/CHANGELOG.md b/CHANGELOG.md index e5a5abab..e4267f9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/doc/conf.py b/doc/conf.py index a68eaa55..656c7291 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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): - app.add_directive('deprecated', VersionDirective) - app.add_directive('versionadded', VersionDirective) - app.add_directive('versionchanged', VersionDirective) +# 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"): -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) + from typing import List + from docutils.nodes import Node + from sphinx.domains.changeset import VersionChange - if completed.returncode != 0: - self.arguments[0] += " (unreleased)" + def setup(app): + app.add_directive('deprecated', VersionDirective) + app.add_directive('versionadded', VersionDirective) + app.add_directive('versionchanged', VersionDirective) - return super().run() + 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]: + directive_version = packaging.version.parse(self.arguments[0]) + + if directive_version > version_txt: + self.arguments[0] += " (unreleased)" + + return super().run() diff --git a/include/x11/tray_client.hpp b/include/x11/tray_client.hpp index d74c9546..eb2593fa 100644 --- a/include/x11/tray_client.hpp +++ b/include/x11/tray_client.hpp @@ -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 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; diff --git a/include/x11/tray_manager.hpp b/include/x11/tray_manager.hpp index 1228c3a9..bf3732e4 100644 --- a/include/x11/tray_manager.hpp +++ b/include/x11/tray_manager.hpp @@ -34,7 +34,6 @@ using namespace std::chrono_literals; // fwd declarations class connection; -struct xembed_data; class background_manager; class bg_slice; diff --git a/include/x11/xembed.hpp b/include/x11/xembed.hpp index b587bbf2..51407612 100644 --- a/include/x11/xembed.hpp +++ b/include/x11/xembed.hpp @@ -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 diff --git a/src/components/config.cpp b/src/components/config.cpp index a94aaee2..5be2c223 100644 --- a/src/components/config.cpp +++ b/src/components/config.cpp @@ -225,6 +225,10 @@ chrono::duration config::convert(string&& value) const { template <> rgba config::convert(string&& value) const { + if (value.empty()) { + return rgba{}; + } + rgba ret{value}; if (!ret.has_color()) { diff --git a/src/components/controller.cpp b/src/components/controller.cpp index 0fefde36..ccc018fb 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -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 { diff --git a/src/modules/ipc.cpp b/src/modules/ipc.cpp index c8082769..d95ab8dc 100644 --- a/src/modules/ipc.cpp +++ b/src/modules/ipc.cpp @@ -38,18 +38,15 @@ namespace modules { m_actions.emplace(make_pair(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}); diff --git a/src/x11/tray_client.cpp b/src/x11/tray_client.cpp index 9075083f..11d357ee 100644 --- a/src/x11/tray_client.cpp +++ b/src/x11/tray_client.cpp @@ -1,19 +1,15 @@ +#include "x11/tray_client.hpp" + #include #include #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(); - 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 - } + m_connection.clear_area_checked(1, window(), 0, 0, width(), height()); } /** @@ -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()); } } diff --git a/src/x11/tray_manager.cpp b/src/x11/tray_manager.cpp index 5cf503d1..77c2eb5b 100644 --- a/src/x11/tray_manager.cpp +++ b/src/x11/tray_manager.cpp @@ -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,13 +433,21 @@ 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) { - client->clear_window(); + 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(); @@ -477,8 +491,8 @@ void tray_manager::create_window() { << cw_class(XCB_WINDOW_CLASS_INPUT_OUTPUT) << cw_params_backing_store(XCB_BACKING_STORE_WHEN_MAPPED) << cw_params_event_mask(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT - |XCB_EVENT_MASK_STRUCTURE_NOTIFY - |XCB_EVENT_MASK_EXPOSURE) + |XCB_EVENT_MASK_STRUCTURE_NOTIFY + |XCB_EVENT_MASK_EXPOSURE) << cw_params_override_redirect(true); // clang-format on @@ -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(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()); - m_log.trace("tray: Send embbeded notification to client"); - xembed::notify_embedded(m_connection, client->window(), m_tray, client->xembed()->version); + if (client->is_xembed_supported()) { + m_log.trace("tray: Send embbeded notification to client"); + 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(); } } diff --git a/src/x11/xembed.cpp b/src/x11/xembed.cpp index b426d9c4..1f3f27a4 100644 --- a/src/x11/xembed.cpp +++ b/src/x11/xembed.cpp @@ -5,26 +5,40 @@ POLYBAR_NS namespace xembed { + + void info::set(uint32_t* data) { + version = data[0]; + flags = data[1]; + } + + uint32_t info::get_version() const { + return version; + } + + uint32_t info::get_flags() const { + return flags; + } + + bool info::is_mapped() const { + return (flags & XEMBED_MAPPED) == XEMBED_MAPPED; + } + /** * Query _XEMBED_INFO for the given window + * + * \return Whether valid XEMBED info was found */ - 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); + 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) { - throw application_error("Invalid _XEMBED_INFO for window " + conn.id(win)); + return false; } - std::vector xembed_data{info.value().begin(), info.value().end()}; + std::vector xembed_data{info.value().begin(), info.value().end()}; + data.set(xembed_data.data()); - data->xembed = _XEMBED; - data->xembed_info = _XEMBED_INFO; - - data->time = XCB_CURRENT_TIME; - data->flags = xembed_data[1]; - data->version = xembed_data[0]; - - return 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)); } /** diff --git a/version.txt b/version.txt index 2bd23581..1addef8a 100644 --- a/version.txt +++ b/version.txt @@ -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