diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 3b70f1ca..fb6170a1 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -14,13 +14,14 @@ POLYBAR_NS // fwd +class screen; class tray_manager; class logger; class renderer; class bar : public xpp::event::sink { public: - explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr tray_manager); + explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr screen, unique_ptr tray_manager); ~bar(); void bootstrap(bool nodraw = false); @@ -45,12 +46,11 @@ class bar : public xpp::event::sink m_screen; unique_ptr m_tray; unique_ptr m_renderer; - xcb_screen_t* m_screen; xcb_window_t m_window; - bar_settings m_opts; alignment m_trayalign{alignment::NONE}; diff --git a/include/components/screen.hpp b/include/components/screen.hpp new file mode 100644 index 00000000..89acff4f --- /dev/null +++ b/include/components/screen.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "common.hpp" +#include "components/types.hpp" +#include "x11/events.hpp" +#include "x11/window.hpp" + +POLYBAR_NS + +// fwd +class config; +class logger; +class connection; + +class screen : public xpp::event::sink { + public: + explicit screen(connection& conn, const logger& logger, const config& conf); + ~screen(); + + struct size size() const { + return m_size; + } + + xcb_window_t root() const { + return m_root; + } + + protected: + void handle(const evt::randr_screen_change_notify& evt); + + private: + connection& m_connection; + const logger& m_log; + const config& m_conf; + + xcb_window_t m_root; + xcb_window_t m_proxy; + + struct size m_size{0U, 0U}; + bool m_sigraised{false}; +}; + +di::injector> configure_screen(); + +POLYBAR_NS_END diff --git a/include/x11/connection.hpp b/include/x11/connection.hpp index aec3a685..3d87e3b5 100644 --- a/include/x11/connection.hpp +++ b/include/x11/connection.hpp @@ -69,6 +69,30 @@ class connection : public xpp_connection { virtual ~connection() {} + template + void wait_for_response(function check_event) { + auto fd = get_file_descriptor(); + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + while (true) { + if (select(fd + 1, &fds, nullptr, nullptr, nullptr) > 0) { + shared_ptr evt; + + if ((evt = poll_for_event()) && evt->response_type == ResponseType) { + if (check_event(reinterpret_cast(*(evt.get())))) { + break; + } + } + } + + if (connection_has_error()) { + break; + } + } + } + void preload_atoms(); void query_extensions(); @@ -77,6 +101,9 @@ class connection : public xpp_connection { xcb_screen_t* screen(); + void ensure_event_mask(xcb_window_t win, uint32_t event); + void clear_event_mask(xcb_window_t win); + shared_ptr make_client_message(xcb_atom_t type, xcb_window_t target) const; void send_client_message(const shared_ptr& message, xcb_window_t target, diff --git a/include/x11/window.hpp b/include/x11/window.hpp index 71a9aa54..d3d96a1b 100644 --- a/include/x11/window.hpp +++ b/include/x11/window.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "common.hpp" diff --git a/src/components/bar.cpp b/src/components/bar.cpp index e93f4583..ad041e4c 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -3,6 +3,7 @@ #include "components/bar.hpp" #include "components/parser.hpp" #include "components/renderer.hpp" +#include "components/screen.hpp" #include "components/signals.hpp" #include "utils/bspwm.hpp" #include "utils/color.hpp" @@ -34,6 +35,7 @@ di::injector> configure_bar() { configure_connection(), configure_config(), configure_logger(), + configure_screen(), configure_tray_manager()); // clang-format on } @@ -41,12 +43,9 @@ di::injector> configure_bar() { /** * Construct bar instance */ -bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr tray_manager) - : m_connection(conn) - , m_conf(config) - , m_log(logger) - , m_tray(forward(tray_manager)) - , m_screen(conn.screen()) {} +bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr screen, + unique_ptr tray_manager) + : m_connection(conn), m_conf(config), m_log(logger), m_screen(move(screen)), m_tray(move(tray_manager)) {} /** * Cleanup signal handlers and destroy the bar window @@ -465,7 +464,7 @@ void bar::setup_monitor() { m_log.trace("bar: Create monitor from matching X RandR output"); auto strict = m_conf.get(m_conf.bar_section(), "monitor-strict", false); - auto monitors = randr_util::get_monitors(m_connection, m_screen->root, strict); + auto monitors = randr_util::get_monitors(m_connection, m_screen->root(), strict); if (monitors.empty()) { throw application_error("No monitors found"); @@ -534,7 +533,7 @@ void bar::restack_window() { * and moving it to the correct position */ void bar::reconfigure_window() { - auto geom = m_connection.get_geometry(m_screen->root); + auto geom = m_connection.get_geometry(m_screen->root()); auto w = m_opts.size.w + m_opts.offset.x; auto h = m_opts.size.h + m_opts.offset.y; auto x = m_opts.pos.x; diff --git a/src/components/screen.cpp b/src/components/screen.cpp new file mode 100644 index 00000000..32222835 --- /dev/null +++ b/src/components/screen.cpp @@ -0,0 +1,86 @@ +#include +#include + +#include "components/config.hpp" +#include "components/logger.hpp" +#include "components/screen.hpp" +#include "components/types.hpp" +#include "x11/connection.hpp" +#include "x11/randr.hpp" +#include "x11/winspec.hpp" + +POLYBAR_NS + +/** + * Configure injection module + */ +di::injector> configure_screen() { + return di::make_injector(configure_connection(), configure_logger(), configure_config()); +} + +/** + * Construct screen instance + */ +screen::screen(connection& conn, const logger& logger, const config& conf) + : m_connection(conn) + , m_log(logger) + , m_conf(conf) + , m_root(conn.root()) + , m_size({conn.screen()->width_in_pixels, conn.screen()->height_in_pixels}) { + // Check if the reloading has been disabled by the user + if (!m_conf.get("settings", "screenchange-reload", false)) { + return; + } + + // clang-format off + m_proxy = winspec(m_connection) + << cw_size(1U, 1U) + << cw_pos(-1, -1) + << cw_parent(m_root) + << cw_params_override_redirect(true) + << cw_params_event_mask(XCB_EVENT_MASK_PROPERTY_CHANGE) + << cw_flush(true); + // clang-format on + + // Receive randr events + m_connection.randr().select_input(m_proxy, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE); + m_connection.ensure_event_mask(m_root, XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY); + + // Create window used as event proxy + m_connection.map_window(m_proxy); + m_connection.flush(); + + // Wait until the proxy window has been mapped + using evt = xcb_map_notify_event_t; + m_connection.wait_for_response([&](const evt& evt) -> bool { return evt.window == m_proxy; }); + m_connection.clear_event_mask(m_root); + + // Finally attach the sink the process randr events + m_connection.attach_sink(this, 1); +} + +/** + * Deconstruct screen instance + */ +screen::~screen() { + m_connection.detach_sink(this, 1); + + if (m_proxy) { + m_connection.destroy_window(m_proxy); + } +} + +/** + * Handle XCB_RANDR_SCREEN_CHANGE_NOTIFY events + * + * If the screen dimensions have changed we raise USR1 to trigger a reload + */ +void screen::handle(const evt::randr_screen_change_notify& evt) { + if (!m_sigraised && evt->request_window == m_proxy) { + m_log.warn("randr_screen_change_notify (%ux%u)... reloading", evt->width, evt->height); + m_sigraised = true; + kill(getpid(), SIGUSR1); + } +} + +POLYBAR_NS_END diff --git a/src/x11/connection.cpp b/src/x11/connection.cpp index c84faf91..57d0d217 100644 --- a/src/x11/connection.cpp +++ b/src/x11/connection.cpp @@ -101,6 +101,26 @@ xcb_screen_t* connection::screen() { return m_screen; } +/** + * Add given event to the event mask unless already added + */ +void connection::ensure_event_mask(xcb_window_t win, uint32_t event) { + auto attributes = get_window_attributes(win); + uint32_t mask{attributes->your_event_mask | event}; + + if (!(attributes->your_event_mask & event)) { + change_window_attributes(win, XCB_CW_EVENT_MASK, &mask); + } +} + +/** + * Clear event mask for the given window + */ +void connection::clear_event_mask(xcb_window_t win) { + uint32_t mask{XCB_EVENT_MASK_NO_EVENT}; + change_window_attributes(win, XCB_CW_EVENT_MASK, &mask); +} + /** * Creates an instance of shared_ptr */ diff --git a/src/x11/window.cpp b/src/x11/window.cpp index 11b92686..d0f65bc3 100644 --- a/src/x11/window.cpp +++ b/src/x11/window.cpp @@ -41,12 +41,7 @@ window window::change_event_mask(uint32_t mask) { * Add given event to the event mask unless already added */ window window::ensure_event_mask(uint32_t event) { - auto attributes = get_attributes(); - - if ((attributes->your_event_mask & event) != event) { - change_event_mask(attributes->your_event_mask | event); - } - + connection().ensure_event_mask(*this, event); return *this; }