From 1d4e30c4be672f2569488a8f23fd9b91a70beffc Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Tue, 13 Sep 2022 14:21:36 +0200 Subject: [PATCH] fix: Handle X events before polling for IO (#2820) * Use m_connection.poll_for_event * fix: Handle X events before polling for IO Polling the XCB file descriptor for X events doesn't detect events that are already in XCB's event queue but not yet handled. If this happens, the eventloop polls for IO and the queued events wait until another event arrives, causing some desyncs in the bar. This can easily happen if something (e.g. a click event) triggers some xcb calls, which as a consequence buffer some incoming events. We "fix" this by adding a libuv prepare handle (which runs right before polling for IO) that processes pending X events. --- include/components/eventloop.hpp | 18 ++++++++++++++++-- lib/xpp | 2 +- src/components/controller.cpp | 17 ++++++++++++++--- src/components/eventloop.cpp | 20 +++++++++++++++++++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/include/components/eventloop.hpp b/include/components/eventloop.hpp index 1a3610aa..7c1908e2 100644 --- a/include/components/eventloop.hpp +++ b/include/components/eventloop.hpp @@ -390,12 +390,27 @@ namespace eventloop { cb_connect connect_callback; }; + class PrepareHandle : public Handle { + public: + using Handle::Handle; + using cb = cb_void; + + void init(); + void start(cb user_cb); + + private: + static void connect_cb(uv_connect_t* req, int status); + + cb callback; + }; + class loop : public non_copyable_mixin, public non_movable_mixin { public: loop(); ~loop(); void run(); void stop(); + uint64_t now() const; template H& handle(Args... args) { @@ -404,13 +419,12 @@ namespace eventloop { return ptr->leak(std::move(ptr)); } - protected: uv_loop_t* get() const; private: std::unique_ptr m_loop{nullptr}; }; -} // namespace eventloop +} // namespace eventloop POLYBAR_NS_END diff --git a/lib/xpp b/lib/xpp index 7a9960bb..343dc77d 160000 --- a/lib/xpp +++ b/lib/xpp @@ -1 +1 @@ -Subproject commit 7a9960bbb912f0ed66929c978aaeb1c30acf4bfd +Subproject commit 343dc77d50bedab3c84b6e7cab70e1af2309eb0e diff --git a/src/components/controller.cpp b/src/components/controller.cpp index ba00ef1a..9fff6adb 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -171,7 +171,7 @@ void controller::conn_cb() { } shared_ptr evt{}; - while ((evt = shared_ptr(xcb_poll_for_event(m_connection), free)) != nullptr) { + while ((evt = m_connection.poll_for_event()) != nullptr) { try { m_connection.dispatch_event(evt); } catch (xpp::connection_error& err) { @@ -247,14 +247,25 @@ void controller::read_events(bool confwatch) { m_log.info("Entering event loop (thread-id=%lu)", this_thread::get_id()); try { - auto& poll_handle = m_loop.handle(m_connection.get_file_descriptor()); - poll_handle.start( + auto& x_poll_handle = m_loop.handle(m_connection.get_file_descriptor()); + x_poll_handle.start( UV_READABLE, [this](const auto&) { conn_cb(); }, [this](const auto& e) { m_log.err("libuv error while polling X connection: "s + uv_strerror(e.status)); stop(false); }); + auto& x_prepare_handle = m_loop.handle(); + x_prepare_handle.start([this]() { + /* + * We have to also handle events in the prepare handle (which runs right + * before polling for IO) to process any already queued X events which + * wouldn't trigger the uv_poll handle. + */ + conn_cb(); + m_connection.flush(); + }); + for (auto s : {SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGALRM}) { auto& signal_handle = m_loop.handle(); signal_handle.start(s, [this](const auto& e) { signal_handler(e.signum); }); diff --git a/src/components/eventloop.cpp b/src/components/eventloop.cpp index 623e3994..e48f7029 100644 --- a/src/components/eventloop.cpp +++ b/src/components/eventloop.cpp @@ -38,6 +38,9 @@ namespace eventloop { case UV_NAMED_PIPE: static_cast(handle->data)->close(); break; + case UV_PREPARE: + static_cast(handle->data)->close(); + break; default: assert(false); } @@ -157,6 +160,17 @@ namespace eventloop { } // }}} + // PrepareHandle {{{ + void PrepareHandle::init() { + UV(uv_prepare_init, loop(), get()); + } + + void PrepareHandle::start(cb user_cb) { + this->callback = user_cb; + UV(uv_prepare_start, get(), void_event_cb<&PrepareHandle::callback>); + } + // }}} + // eventloop {{{ static void close_walk_cb(uv_handle_t* handle, void*) { if (!uv_is_closing(handle)) { @@ -201,11 +215,15 @@ namespace eventloop { uv_stop(m_loop.get()); } + uint64_t loop::now() const { + return uv_now(m_loop.get()); + } + uv_loop_t* loop::get() const { return m_loop.get(); } // }}} -} // namespace eventloop +} // namespace eventloop POLYBAR_NS_END