Integrate bar taskqueue into eventloop (#2510)

* Remove DEBUG_SHADED

Was disabled by default AND behind an #if 0

* Make TimerHandle expose more libuv functions

* Prepare for moving double clicks into eventloop

* Make eventloop available to bar

* Remove bar mutex

Everything in the bar is now in the same thread

* Move double-click handling to eventloop

* Extract double click deferred function into method

* Stop throttling clicks

* Increase double click interval to 400 and add option

double-click-interval in the bar section

Closes #1441

* Implement dimming using timer handles

* Remove taskqueue

* Remove unused dependencies

* Cleanup & Comments
This commit is contained in:
Patrick Ziegler 2021-09-27 17:35:45 +02:00 committed by GitHub
parent 55eb19fdc7
commit 8afd5b71df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 156 additions and 471 deletions

View File

@ -59,6 +59,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
triggers if they happen directly after one another, leading to only a single
bar update.
### Removed
- `DEBUG_SHADED` cmake variable and its associated functionality.
### Added
- `drawtypes/ramp`: Add support for ramp weights.
([1750](https://github.com/polybar/polybar/issues/1750))
@ -114,6 +117,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([`#2427`](https://github.com/polybar/polybar/issues/2427))
- `custom/ipc`: `send` action to send arbitrary strings to be displayed in the module.
([`#2455`](https://github.com/polybar/polybar/issues/2455))
- Added `double-click-interval` setting to the bar section to control the time
interval in which a double-click is recognized. Defaults to 400 (ms)
([`#1441`](https://github.com/polybar/polybar/issues/1441))
### Changed
- We rewrote polybar's main event loop. This shouldn't change any behavior for
@ -130,6 +136,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `internal/network`:
- Increased precision for upload and download speeds: 0 decimal places for
KB/s (as before), 1 for MB/s and 2 for GB/s.
- Clicks arriving in close succession, no longer get dropped. Before polybar
would drop any click that arrived within 5ms of the previous one.
- Increased the double click interval from 150ms to 400ms.
### Fixed
- Trailing space after the layout label when indicators are empty and made sure right amount

View File

@ -55,6 +55,5 @@ if (BUILD_LIBPOLY)
colored_option(" Trace logging (verbose)" DEBUG_LOGGER_VERBOSE)
colored_option(" Draw clickable areas" DEBUG_HINTS)
colored_option(" Print fc-match details" DEBUG_FONTCONFIG)
colored_option(" Enable window shading" DEBUG_SHADED)
endif()
endif()

View File

@ -5,6 +5,7 @@
#include <mutex>
#include "common.hpp"
#include "components/eventloop.hpp"
#include "components/types.hpp"
#include "errors.hpp"
#include "events/signal_fwd.hpp"
@ -23,7 +24,6 @@ class connection;
class logger;
class renderer;
class screen;
class taskqueue;
class tray_manager;
namespace tags {
@ -55,8 +55,7 @@ inline double geom_format_to_pixels(std::string str, double max) {
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify,
evt::leave_notify, evt::motion_notify, evt::destroy_notify, evt::client_message, evt::configure_notify>,
public signal_receiver<SIGN_PRIORITY_BAR, signals::ui::tick, signals::ui::shade_window,
signals::ui::unshade_window, signals::ui::dim_window
public signal_receiver<SIGN_PRIORITY_BAR, signals::ui::dim_window
#if WITH_XCURSOR
,
signals::ui::cursor_change
@ -64,11 +63,11 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
> {
public:
using make_type = unique_ptr<bar>;
static make_type make(bool only_initialize_values = false);
static make_type make(eventloop&, bool only_initialize_values = false);
explicit bar(connection&, signal_emitter&, const config&, const logger&, unique_ptr<screen>&&,
explicit bar(connection&, signal_emitter&, const config&, const logger&, eventloop&, unique_ptr<screen>&&,
unique_ptr<tray_manager>&&, unique_ptr<tags::dispatch>&&, unique_ptr<tags::action_context>&&,
unique_ptr<taskqueue>&&, bool only_initialize_values);
bool only_initialize_values);
~bar();
const bar_settings settings() const;
@ -90,6 +89,8 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
void reconfigure_wm_hints();
void broadcast_visibility();
void trigger_click(mousebtn btn, int pos);
void handle(const evt::client_message& evt) override;
void handle(const evt::destroy_notify& evt) override;
void handle(const evt::enter_notify& evt) override;
@ -100,9 +101,6 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
void handle(const evt::property_notify& evt) override;
void handle(const evt::configure_notify& evt) override;
bool on(const signals::ui::unshade_window&) override;
bool on(const signals::ui::shade_window&) override;
bool on(const signals::ui::tick&) override;
bool on(const signals::ui::dim_window&) override;
#if WITH_XCURSOR
bool on(const signals::ui::cursor_change&) override;
@ -113,29 +111,26 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
signal_emitter& m_sig;
const config& m_conf;
const logger& m_log;
eventloop& m_loop;
unique_ptr<screen> m_screen;
unique_ptr<tray_manager> m_tray;
unique_ptr<renderer> m_renderer;
unique_ptr<tags::dispatch> m_dispatch;
unique_ptr<tags::action_context> m_action_ctxt;
unique_ptr<taskqueue> m_taskqueue;
bar_settings m_opts{};
string m_lastinput{};
std::mutex m_mutex{};
std::atomic<bool> m_dblclicks{false};
bool m_dblclicks{false};
mousebtn m_buttonpress_btn{mousebtn::NONE};
int m_buttonpress_pos{0};
#if WITH_XCURSOR
int m_motion_pos{0};
#endif
event_timer m_buttonpress{0L, 5L};
event_timer m_doubleclick{0L, 150L};
double m_anim_step{0.0};
TimerHandle_t m_leftclick_timer{m_loop.timer_handle(nullptr)};
TimerHandle_t m_middleclick_timer{m_loop.timer_handle(nullptr)};
TimerHandle_t m_rightclick_timer{m_loop.timer_handle(nullptr)};
TimerHandle_t m_dim_timer{m_loop.timer_handle(nullptr)};
bool m_visible{true};
};

View File

@ -2,7 +2,6 @@
#include <atomic>
#include <mutex>
#include <thread>
#include "common.hpp"
#include "components/eventloop.hpp"
@ -42,7 +41,7 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
using make_type = unique_ptr<controller>;
static make_type make(unique_ptr<ipc>&& ipc);
explicit controller(connection&, signal_emitter&, const logger&, const config&, unique_ptr<bar>&&, unique_ptr<ipc>&&);
explicit controller(connection&, signal_emitter&, const logger&, const config&, unique_ptr<ipc>&&);
~controller();
bool run(bool writeback, string snapshot_dst, bool confwatch);
@ -98,15 +97,14 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, signals::eve
signal_emitter& m_sig;
const logger& m_log;
const config& m_conf;
unique_ptr<eventloop> m_loop;
unique_ptr<bar> m_bar;
unique_ptr<ipc> m_ipc;
std::unique_ptr<eventloop> eloop;
/**
* Once this is set to true, 'eloop' and any uv handles can be used.
* Once this is set to true, 'm_loop' and any uv handles can be used.
*/
std::atomic_bool m_eloop_ready{false};
std::atomic_bool m_loop_ready{false};
/**
* \brief Async handle to notify the eventloop

View File

@ -45,11 +45,19 @@ struct UVHandleGeneric {
}
void close() {
if (handle && !uv_is_closing((uv_handle_t*)handle)) {
if (!is_closing()) {
uv_close((uv_handle_t*)handle, close_callback);
}
}
bool is_closing() {
return !handle || uv_is_closing((uv_handle_t*)handle);
}
bool is_active() {
return uv_is_active((uv_handle_t*)handle) != 0;
}
void cleanup_resources() {
if (handle) {
delete handle;
@ -109,7 +117,8 @@ struct PipeHandle : public UVHandleGeneric<uv_pipe_t, uv_stream_t, ssize_t, cons
struct TimerHandle : public UVHandle<uv_timer_t> {
TimerHandle(uv_loop_t* loop, function<void(void)> fun);
void start(uint64_t timeout, uint64_t repeat);
void start(uint64_t timeout, uint64_t repeat, function<void(void)> new_cb = function<void(void)>(nullptr));
void stop();
};
struct AsyncHandle : public UVHandle<uv_async_t> {
@ -121,8 +130,8 @@ using SignalHandle_t = std::unique_ptr<SignalHandle>;
using PollHandle_t = std::unique_ptr<PollHandle>;
using FSEventHandle_t = std::unique_ptr<FSEventHandle>;
using PipeHandle_t = std::unique_ptr<PipeHandle>;
using TimerHandle_t = std::unique_ptr<TimerHandle>;
// shared_ptr because we need a reference outside to call send
// shared_ptr because we also return the pointer in order to call methods on it
using TimerHandle_t = std::shared_ptr<TimerHandle>;
using AsyncHandle_t = std::shared_ptr<AsyncHandle>;
class eventloop {
@ -136,7 +145,7 @@ class eventloop {
void fs_event_handle(const string& path, function<void(const char*, uv_fs_event)> fun, function<void(int)> err_cb);
void pipe_handle(
const string& path, function<void(const string)> fun, function<void(void)> eof_cb, function<void(int)> err_cb);
void timer_handle(uint64_t timeout, uint64_t repeat, function<void(void)> fun);
TimerHandle_t timer_handle(function<void(void)> fun);
AsyncHandle_t async_handle(function<void(void)> fun);
protected:

View File

@ -1,62 +0,0 @@
#pragma once
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include "common.hpp"
#include "utils/mixins.hpp"
POLYBAR_NS
namespace chrono = std::chrono;
using namespace std::chrono_literals;
class taskqueue : non_copyable_mixin<taskqueue> {
public:
struct deferred {
using clock = chrono::high_resolution_clock;
using duration = chrono::milliseconds;
using timepoint = chrono::time_point<clock, duration>;
using callback = function<void(size_t remaining)>;
explicit deferred(string id, timepoint now, duration wait, callback fn, size_t count)
: id(move(id)), func(move(fn)), now(move(now)), wait(move(wait)), count(move(count)) {}
const string id;
const callback func;
timepoint now;
duration wait;
size_t count;
};
public:
using make_type = unique_ptr<taskqueue>;
static make_type make();
explicit taskqueue();
~taskqueue();
void defer(
string id, deferred::duration ms, deferred::callback fn, deferred::duration offset = 0ms, size_t count = 1);
void defer_unique(
string id, deferred::duration ms, deferred::callback fn, deferred::duration offset = 0ms, size_t count = 1);
bool exist(const string& id);
bool purge(const string& id);
protected:
void tick();
private:
std::thread m_thread;
std::mutex m_lock{};
std::condition_variable m_hold;
std::atomic_bool m_active{true};
vector<unique_ptr<deferred>> m_deferred;
};
POLYBAR_NS_END

View File

@ -46,6 +46,19 @@ enum class mousebtn {
BTN_COUNT,
};
static inline mousebtn mousebtn_get_double(mousebtn btn) {
switch (btn) {
case mousebtn::LEFT:
return mousebtn::DOUBLE_LEFT;
case mousebtn::MIDDLE:
return mousebtn::DOUBLE_MIDDLE;
case mousebtn::RIGHT:
return mousebtn::DOUBLE_RIGHT;
default:
return mousebtn::NONE;
}
}
enum class strut {
LEFT = 0,
RIGHT,
@ -146,6 +159,8 @@ struct bar_settings {
bool override_redirect{false};
int double_click_interval{400};
string cursor{};
string cursor_click{};
string cursor_scroll{};
@ -155,12 +170,6 @@ struct bar_settings {
bool dimmed{false};
double dimvalue{1.0};
bool shaded{false};
struct size shade_size {
1U, 1U
};
position shade_pos{1U, 1U};
const xcb_rectangle_t inner_area(bool abspos = false) const {
xcb_rectangle_t rect = this->outer_area(abspos);

View File

@ -80,9 +80,6 @@ namespace signals {
struct changed : public detail::base_signal<changed> {
using base_type::base_type;
};
struct tick : public detail::base_signal<tick> {
using base_type::base_type;
};
struct button_press : public detail::value_signal<button_press, string> {
using base_type::base_type;
};
@ -95,12 +92,6 @@ namespace signals {
struct dim_window : public detail::value_signal<dim_window, double> {
using base_type::base_type;
};
struct shade_window : public detail::base_signal<shade_window> {
using base_type::base_type;
};
struct unshade_window : public detail::base_signal<unshade_window> {
using base_type::base_type;
};
struct request_snapshot : public detail::value_signal<request_snapshot, string> {
using base_type::base_type;
};

View File

@ -27,13 +27,10 @@ namespace signals {
} // namespace ipc
namespace ui {
struct changed;
struct tick;
struct button_press;
struct cursor_change;
struct visibility_change;
struct dim_window;
struct shade_window;
struct unshade_window;
struct request_snapshot;
struct update_background;
struct update_geometry;

View File

@ -41,7 +41,6 @@ extern const char* const APP_VERSION;
#cmakedefine DEBUG_LOGGER_VERBOSE
#cmakedefine DEBUG_HINTS
#cmakedefine DEBUG_WHITESPACE
#cmakedefine DEBUG_SHADED
#cmakedefine DEBUG_FONTCONFIG
#endif

View File

@ -66,7 +66,6 @@ if(BUILD_LIBPOLY)
${src_dir}/components/logger.cpp
${src_dir}/components/renderer.cpp
${src_dir}/components/screen.cpp
${src_dir}/components/taskqueue.cpp
${src_dir}/components/eventloop.cpp
${src_dir}/drawtypes/animation.cpp

View File

@ -5,7 +5,6 @@
#include "components/config.hpp"
#include "components/renderer.hpp"
#include "components/screen.hpp"
#include "components/taskqueue.hpp"
#include "components/types.hpp"
#include "drawtypes/label.hpp"
#include "events/signal.hpp"
@ -37,7 +36,7 @@ using namespace signals::ui;
/**
* Create instance
*/
bar::make_type bar::make(bool only_initialize_values) {
bar::make_type bar::make(eventloop& loop, bool only_initialize_values) {
auto action_ctxt = make_unique<tags::action_context>();
// clang-format off
@ -46,11 +45,11 @@ bar::make_type bar::make(bool only_initialize_values) {
signal_emitter::make(),
config::make(),
logger::make(),
loop,
screen::make(),
tray_manager::make(),
tags::dispatch::make(*action_ctxt),
std::move(action_ctxt),
taskqueue::make(),
only_initialize_values);
// clang-format on
}
@ -60,18 +59,18 @@ bar::make_type bar::make(bool only_initialize_values) {
*
* TODO: Break out all tray handling
*/
bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger,
bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, eventloop& loop,
unique_ptr<screen>&& screen, unique_ptr<tray_manager>&& tray_manager, unique_ptr<tags::dispatch>&& dispatch,
unique_ptr<tags::action_context>&& action_ctxt, unique_ptr<taskqueue>&& taskqueue, bool only_initialize_values)
unique_ptr<tags::action_context>&& action_ctxt, bool only_initialize_values)
: m_connection(conn)
, m_sig(emitter)
, m_conf(config)
, m_log(logger)
, m_loop(loop)
, m_screen(forward<decltype(screen)>(screen))
, m_tray(forward<decltype(tray_manager)>(tray_manager))
, m_dispatch(forward<decltype(dispatch)>(dispatch))
, m_action_ctxt(forward<decltype(action_ctxt)>(action_ctxt))
, m_taskqueue(forward<decltype(taskqueue)>(taskqueue)) {
, m_action_ctxt(forward<decltype(action_ctxt)>(action_ctxt)) {
string bs{m_conf.section()};
// Get available RandR outputs
@ -179,6 +178,8 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.module_margin.left = m_conf.get(bs, "module-margin-left", margin);
m_opts.module_margin.right = m_conf.get(bs, "module-margin-right", margin);
m_opts.double_click_interval = m_conf.get(bs, "double-click-interval", m_opts.double_click_interval);
if (only_initialize_values) {
return;
}
@ -308,7 +309,6 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
* Cleanup signal handlers and destroy the bar window
*/
bar::~bar() {
std::lock_guard<std::mutex> guard(m_mutex);
m_connection.detach_sink(this, SINK_PRIORITY_BAR);
m_sig.detach(this);
}
@ -327,12 +327,6 @@ const bar_settings bar::settings() const {
* \param force Unless true, do not parse unchanged data
*/
void bar::parse(string&& data, bool force) {
if (!m_mutex.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_mutex, std::adopt_lock);
bool unchanged = data == m_lastinput;
m_lastinput = data;
@ -341,8 +335,6 @@ void bar::parse(string&& data, bool force) {
m_log.trace("bar: Force update");
} else if (!m_visible) {
return m_log.trace("bar: Ignoring update (invisible)");
} else if (m_opts.shaded) {
return m_log.trace("bar: Ignoring update (shaded)");
} else if (unchanged) {
return m_log.trace("bar: Ignoring update (unchanged)");
}
@ -575,6 +567,25 @@ void bar::broadcast_visibility() {
}
}
void bar::trigger_click(mousebtn btn, int pos) {
tags::action_t action = m_action_ctxt->has_action(btn, pos);
if (action != tags::NO_ACTION) {
m_log.trace("Found matching input area");
m_sig.emit(button_press{m_action_ctxt->get_action(action)});
return;
}
for (auto&& action : m_opts.actions) {
if (action.button == btn && !action.command.empty()) {
m_log.trace("Found matching fallback handler");
m_sig.emit(button_press{string{action.command}});
return;
}
}
m_log.info("No matching input area found (btn=%i)", static_cast<int>(btn));
}
/**
* Event handler for XCB_DESTROY_NOTIFY events
*/
@ -601,21 +612,13 @@ void bar::handle(const evt::destroy_notify& evt) {
* _NET_WM_WINDOW_OPACITY atom value
*/
void bar::handle(const evt::enter_notify&) {
#if 0
#ifdef DEBUG_SHADED
if (m_opts.origin == edge::TOP) {
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::unshade_window{}); });
return;
}
#endif
#endif
if (m_opts.dimmed) {
m_taskqueue->defer_unique("window-dim", 25ms, [&](size_t) {
m_dim_timer->start(25, 0, [this]() {
m_opts.dimmed = false;
m_sig.emit(dim_window{1.0});
});
} else if (m_taskqueue->exist("window-dim")) {
m_taskqueue->purge("window-dim");
} else if (m_dim_timer->is_active()) {
m_dim_timer->stop();
}
}
@ -626,20 +629,15 @@ void bar::handle(const evt::enter_notify&) {
* _NET_WM_WINDOW_OPACITY atom value
*/
void bar::handle(const evt::leave_notify&) {
#if 0
#ifdef DEBUG_SHADED
if (m_opts.origin == edge::TOP) {
m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(signals::ui::shade_window{}); });
return;
}
#endif
#endif
// Only trigger dimming, if the dim-value is not fully opaque.
if (m_opts.dimvalue < 1.0) {
if (!m_opts.dimmed) {
m_taskqueue->defer_unique("window-dim", 3s, [&](size_t) {
m_dim_timer->start(3000, 0, [this]() {
m_opts.dimmed = true;
m_sig.emit(dim_window{double(m_opts.dimvalue)});
});
}
}
}
/**
@ -648,12 +646,6 @@ void bar::handle(const evt::leave_notify&) {
* Used to change the cursor depending on the module
*/
void bar::handle(const evt::motion_notify& evt) {
if (!m_mutex.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_mutex, std::adopt_lock);
m_log.trace("bar: Detected motion: %i at pos(%i, %i)", evt->detail, evt->event_x, evt->event_y);
#if WITH_XCURSOR
m_motion_pos = evt->event_x;
@ -735,63 +727,37 @@ void bar::handle(const evt::motion_notify& evt) {
* Used to map mouse clicks to bar actions
*/
void bar::handle(const evt::button_press& evt) {
if (!m_mutex.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_mutex, std::adopt_lock);
if (m_buttonpress.deny(evt->time)) {
return m_log.trace_x("bar: Ignoring button press (throttled)...");
}
m_log.trace("bar: Received button press: %i at pos(%i, %i)", evt->detail, evt->event_x, evt->event_y);
m_buttonpress_btn = static_cast<mousebtn>(evt->detail);
m_buttonpress_pos = evt->event_x;
mousebtn btn = static_cast<mousebtn>(evt->detail);
int pos = evt->event_x;
const auto deferred_fn = [&](size_t) {
tags::action_t action = m_action_ctxt->has_action(m_buttonpress_btn, m_buttonpress_pos);
if (action != tags::NO_ACTION) {
m_log.trace("Found matching input area");
m_sig.emit(button_press{m_action_ctxt->get_action(action)});
return;
}
for (auto&& action : m_opts.actions) {
if (action.button == m_buttonpress_btn && !action.command.empty()) {
m_log.trace("Found matching fallback handler");
m_sig.emit(button_press{string{action.command}});
return;
}
}
m_log.info("No matching input area found (btn=%i)", static_cast<int>(m_buttonpress_btn));
};
const auto check_double = [&](string&& id, mousebtn&& btn) {
if (!m_taskqueue->exist(id)) {
m_doubleclick.event = evt->time;
m_taskqueue->defer(id, taskqueue::deferred::duration{m_doubleclick.offset}, deferred_fn);
} else if (m_doubleclick.deny(evt->time)) {
m_doubleclick.event = 0;
m_buttonpress_btn = btn;
m_taskqueue->defer_unique(id, 0ms, deferred_fn);
/*
* For possible double-clicks we need to delay the triggering of the click by
* the configured interval and if in that time another click arrives, we
* need to trigger a double click.
*/
const auto check_double = [this](TimerHandle_t handle, mousebtn btn, int pos) {
if (!handle->is_active()) {
handle->start(m_opts.double_click_interval, 0, [=]() { trigger_click(btn, pos); });
} else {
handle->stop();
trigger_click(mousebtn_get_double(btn), pos);
}
};
// If there are no double click handlers defined we can
// just by-pass the click timer handling
if (!m_dblclicks) {
deferred_fn(0);
} else if (evt->detail == static_cast<int>(mousebtn::LEFT)) {
check_double("buttonpress-left", mousebtn::DOUBLE_LEFT);
} else if (evt->detail == static_cast<int>(mousebtn::MIDDLE)) {
check_double("buttonpress-middle", mousebtn::DOUBLE_MIDDLE);
} else if (evt->detail == static_cast<int>(mousebtn::RIGHT)) {
check_double("buttonpress-right", mousebtn::DOUBLE_RIGHT);
trigger_click(btn, pos);
} else if (btn == mousebtn::LEFT) {
check_double(m_leftclick_timer, btn, pos);
} else if (btn == mousebtn::MIDDLE) {
check_double(m_middleclick_timer, btn, pos);
} else if (btn == mousebtn::RIGHT) {
check_double(m_rightclick_timer, btn, pos);
} else {
deferred_fn(0);
trigger_click(btn, pos);
}
}
@ -876,110 +842,6 @@ void bar::start() {
broadcast_visibility();
}
bool bar::on(const signals::ui::unshade_window&) {
m_opts.shaded = false;
m_opts.shade_size.w = m_opts.size.w;
m_opts.shade_size.h = m_opts.size.h;
m_opts.shade_pos.x = m_opts.pos.x;
m_opts.shade_pos.y = m_opts.pos.y;
double distance{static_cast<double>(m_opts.shade_size.h - m_connection.get_geometry(m_opts.window)->height)};
double steptime{25.0 / 2.0};
m_anim_step = distance / steptime / 2.0;
m_taskqueue->defer_unique(
"window-shade", 25ms,
[&](size_t remaining) {
if (!m_opts.shaded) {
m_sig.emit(signals::ui::tick{});
}
if (!remaining) {
m_renderer->flush();
}
if (m_opts.dimmed) {
m_opts.dimmed = false;
m_sig.emit(dim_window{1.0});
}
},
taskqueue::deferred::duration{25ms}, 10U);
return true;
}
bool bar::on(const signals::ui::shade_window&) {
taskqueue::deferred::duration offset{2000ms};
if (!m_opts.shaded && m_opts.shade_size.h != m_opts.size.h) {
offset = taskqueue::deferred::duration{25ms};
}
m_opts.shaded = true;
m_opts.shade_size.h = 5;
m_opts.shade_size.w = m_opts.size.w;
m_opts.shade_pos.x = m_opts.pos.x;
m_opts.shade_pos.y = m_opts.pos.y;
if (m_opts.origin == edge::BOTTOM) {
m_opts.shade_pos.y = m_opts.pos.y + m_opts.size.h - m_opts.shade_size.h;
}
double distance{static_cast<double>(m_connection.get_geometry(m_opts.window)->height - m_opts.shade_size.h)};
double steptime{25.0 / 2.0};
m_anim_step = distance / steptime / 2.0;
m_taskqueue->defer_unique(
"window-shade", 25ms,
[&](size_t remaining) {
if (m_opts.shaded) {
m_sig.emit(signals::ui::tick{});
}
if (!remaining) {
m_renderer->flush();
}
if (!m_opts.dimmed) {
m_opts.dimmed = true;
m_sig.emit(dim_window{double{m_opts.dimvalue}});
}
},
move(offset), 10U);
return true;
}
bool bar::on(const signals::ui::tick&) {
auto geom = m_connection.get_geometry(m_opts.window);
if (geom->y == m_opts.shade_pos.y && geom->height == m_opts.shade_size.h) {
return false;
}
unsigned int mask{0};
unsigned int values[7]{0};
xcb_params_configure_window_t params{};
if (m_opts.shade_size.h > geom->height) {
XCB_AUX_ADD_PARAM(&mask, &params, height, static_cast<unsigned int>(geom->height + m_anim_step));
params.height = std::max(1U, std::min(params.height, static_cast<unsigned int>(m_opts.shade_size.h)));
} else if (m_opts.shade_size.h < geom->height) {
XCB_AUX_ADD_PARAM(&mask, &params, height, static_cast<unsigned int>(geom->height - m_anim_step));
params.height = std::max(1U, std::max(params.height, static_cast<unsigned int>(m_opts.shade_size.h)));
}
if (m_opts.shade_pos.y > geom->y) {
XCB_AUX_ADD_PARAM(&mask, &params, y, static_cast<int>(geom->y + m_anim_step));
params.y = std::min(params.y, static_cast<int>(m_opts.shade_pos.y));
} else if (m_opts.shade_pos.y < geom->y) {
XCB_AUX_ADD_PARAM(&mask, &params, y, static_cast<int>(geom->y - m_anim_step));
params.y = std::max(params.y, static_cast<int>(m_opts.shade_pos.y));
}
connection::pack_values(mask, &params, values);
m_connection.configure_window(m_opts.window, mask, values);
m_connection.flush();
return false;
}
bool bar::on(const signals::ui::dim_window& sig) {
m_opts.dimmed = sig.cast() != 1.0;
ewmh_util::set_wm_window_opacity(m_opts.window, sig.cast() * 0xFFFFFFFF);

View File

@ -31,20 +31,21 @@ POLYBAR_NS
* Build controller instance
*/
controller::make_type controller::make(unique_ptr<ipc>&& ipc) {
return std::make_unique<controller>(connection::make(), signal_emitter::make(), logger::make(), config::make(),
bar::make(), forward<decltype(ipc)>(ipc));
return std::make_unique<controller>(
connection::make(), signal_emitter::make(), logger::make(), config::make(), forward<decltype(ipc)>(ipc));
}
/**
* Construct controller
*/
controller::controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config,
unique_ptr<bar>&& bar, unique_ptr<ipc>&& ipc)
controller::controller(
connection& conn, signal_emitter& emitter, const logger& logger, const config& config, unique_ptr<ipc>&& ipc)
: m_connection(conn)
, m_sig(emitter)
, m_log(logger)
, m_conf(config)
, m_bar(forward<decltype(bar)>(bar))
, m_loop(make_unique<eventloop>())
, m_bar(bar::make(*m_loop))
, m_ipc(forward<decltype(ipc)>(ipc)) {
m_conf.ignore_key("settings", "throttle-input-for");
m_conf.ignore_key("settings", "throttle-output");
@ -152,14 +153,14 @@ void controller::trigger_update(bool force) {
}
void controller::trigger_notification() {
if (m_eloop_ready) {
if (m_loop_ready) {
m_notifier->send();
}
}
void controller::stop(bool reload) {
update_reload(reload);
eloop->stop();
m_loop->stop();
}
void controller::conn_cb(uv_poll_event) {
@ -210,7 +211,7 @@ void controller::notifier_handler() {
if (data.quit) {
update_reload(data.reload);
eloop->stop();
m_loop->stop();
return;
}
@ -234,29 +235,23 @@ void controller::screenshot_handler() {
void controller::read_events(bool confwatch) {
m_log.info("Entering event loop (thread-id=%lu)", this_thread::get_id());
if (!m_writeback) {
m_bar->start();
}
try {
eloop = std::make_unique<eventloop>();
eloop->poll_handle(
m_loop->poll_handle(
UV_READABLE, m_connection.get_file_descriptor(), [this](uv_poll_event events) { conn_cb(events); },
[](int status) { throw runtime_error("libuv error while polling X connection: "s + uv_strerror(status)); });
for (auto s : {SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGALRM}) {
eloop->signal_handle(s, [this](int signum) { signal_handler(signum); });
m_loop->signal_handle(s, [this](int signum) { signal_handler(signum); });
}
if (confwatch) {
eloop->fs_event_handle(
m_loop->fs_event_handle(
m_conf.filepath(), [this](const char* path, uv_fs_event events) { confwatch_handler(path, events); },
[this](int err) { m_log.err("libuv error while watching config file for changes: %s", uv_strerror(err)); });
}
if (m_ipc) {
eloop->pipe_handle(
m_loop->pipe_handle(
m_ipc->get_path(), [this](const string payload) { m_ipc->receive_data(payload); },
[this]() { m_ipc->receive_eof(); },
[this](int err) { m_log.err("libuv error while listening to IPC channel: %s", uv_strerror(err)); });
@ -264,19 +259,23 @@ void controller::read_events(bool confwatch) {
if (!m_snapshot_dst.empty()) {
// Trigger a single screenshot after 3 seconds
eloop->timer_handle(3000, 0, [this]() { screenshot_handler(); });
m_loop->timer_handle([this]() { screenshot_handler(); })->start(3000, 0);
}
m_notifier = eloop->async_handle([this]() { notifier_handler(); });
m_notifier = m_loop->async_handle([this]() { notifier_handler(); });
m_eloop_ready.store(true);
m_loop_ready = true;
if (!m_writeback) {
m_bar->start();
}
/*
* Immediately trigger and update so that the bar displays something.
*/
trigger_update(true);
eloop->run();
m_loop->run();
} catch (const exception& err) {
m_log.err("Fatal Error in eventloop: %s", err.what());
stop(false);
@ -284,7 +283,8 @@ void controller::read_events(bool confwatch) {
m_log.info("Eventloop finished");
eloop.reset();
m_loop_ready = false;
m_loop.reset();
}
/**

View File

@ -141,7 +141,7 @@ void PipeHandle::read_cb(ssize_t nread, const uv_buf_t* buf) {
*
* We reuse the memory for the underlying uv handle
*/
if (!uv_is_closing((uv_handle_t*)handle)) {
if (!is_closing()) {
uv_close((uv_handle_t*)handle, [](uv_handle_t* handle) {
PipeHandle* This = static_cast<PipeHandle*>(handle->data);
UV(uv_pipe_init, This->loop(), This->handle, false);
@ -158,9 +158,17 @@ TimerHandle::TimerHandle(uv_loop_t* loop, function<void(void)> fun) : UVHandle(f
UV(uv_timer_init, loop, handle);
}
void TimerHandle::start(uint64_t timeout, uint64_t repeat) {
void TimerHandle::start(uint64_t timeout, uint64_t repeat, function<void(void)> new_cb) {
if (new_cb) {
this->func = new_cb;
}
UV(uv_timer_start, handle, callback, timeout, repeat);
}
void TimerHandle::stop() {
UV(uv_timer_stop, handle);
}
// }}}
// AsyncHandle {{{
@ -243,9 +251,9 @@ void eventloop::pipe_handle(
m_pipe_handles.back()->start();
}
void eventloop::timer_handle(uint64_t timeout, uint64_t repeat, function<void(void)> fun) {
m_timer_handles.emplace_back(std::make_unique<TimerHandle>(get(), fun));
m_timer_handles.back()->start(timeout, repeat);
TimerHandle_t eventloop::timer_handle(function<void(void)> fun) {
m_timer_handles.emplace_back(std::make_shared<TimerHandle>(get(), fun));
return m_timer_handles.back();
}
AsyncHandle_t eventloop::async_handle(function<void(void)> fun) {

View File

@ -381,25 +381,6 @@ void renderer::flush() {
highlight_clickable_areas();
#if 0
#ifdef DEBUG_SHADED
if (m_bar.shaded && m_bar.origin == edge::TOP) {
m_log.trace_x(
"renderer: copy pixmap (shaded=1, geom=%dx%d+%d+%d)", m_rect.width, m_rect.height, m_rect.x, m_rect.y);
auto geom = m_connection.get_geometry(m_window);
auto x1 = 0;
auto y1 = m_rect.height - m_bar.shade_size.h - m_rect.y - geom->height;
auto x2 = m_rect.x;
auto y2 = m_rect.y;
auto w = m_rect.width;
auto h = m_rect.height - m_bar.shade_size.h + geom->height;
m_connection.copy_area(m_pixmap, m_window, m_gcontext, x1, y1, x2, y2, w, h);
m_connection.flush();
return;
}
#endif
#endif
m_surface->flush();
m_connection.copy_area(m_pixmap, m_window, m_gcontext, 0, 0, 0, 0, m_bar.size.w, m_bar.size.h);
m_connection.flush();

View File

@ -1,109 +0,0 @@
#include "components/taskqueue.hpp"
#include <algorithm>
POLYBAR_NS
taskqueue::make_type taskqueue::make() {
return std::make_unique<taskqueue>();
}
taskqueue::taskqueue() {
m_thread = std::thread([&] {
while (m_active) {
std::unique_lock<std::mutex> guard(m_lock);
if (m_deferred.empty()) {
m_hold.wait(guard);
} else {
auto now = deferred::clock::now();
auto wait = m_deferred.front()->now + m_deferred.front()->wait;
for (auto&& task : m_deferred) {
auto when = task->now + task->wait;
if (when < wait) {
wait = move(when);
}
}
if (wait > now) {
m_hold.wait_for(guard, wait - now);
}
}
if (!m_deferred.empty()) {
guard.unlock();
tick();
}
}
});
}
taskqueue::~taskqueue() {
if (m_active && m_thread.joinable()) {
m_active = false;
m_hold.notify_all();
m_thread.join();
}
}
void taskqueue::defer(
string id, deferred::duration ms, deferred::callback fn, deferred::duration offset, size_t count) {
std::unique_lock<std::mutex> guard(m_lock);
deferred::timepoint now{chrono::time_point_cast<deferred::duration>(deferred::clock::now() + move(offset))};
m_deferred.emplace_back(make_unique<deferred>(move(id), move(now), move(ms), move(fn), move(count)));
guard.unlock();
m_hold.notify_one();
}
void taskqueue::defer_unique(
string id, deferred::duration ms, deferred::callback fn, deferred::duration offset, size_t count) {
purge(id);
std::unique_lock<std::mutex> guard(m_lock);
deferred::timepoint now{chrono::time_point_cast<deferred::duration>(deferred::clock::now() + move(offset))};
m_deferred.emplace_back(make_unique<deferred>(move(id), move(now), move(ms), move(fn), move(count)));
guard.unlock();
m_hold.notify_one();
}
void taskqueue::tick() {
if (!m_lock.try_lock()) {
return;
}
std::unique_lock<std::mutex> guard(m_lock, std::adopt_lock);
auto now = chrono::time_point_cast<deferred::duration>(deferred::clock::now());
vector<pair<deferred::callback, size_t>> cbs;
for (auto it = m_deferred.rbegin(); it != m_deferred.rend(); ++it) {
auto& task = *it;
if (task->now + task->wait > now) {
continue;
} else if (task->count--) {
cbs.emplace_back(make_pair(task->func, task->count));
task->now = now;
} else {
m_deferred.erase(std::remove_if(m_deferred.begin(), m_deferred.end(),
[&](const unique_ptr<deferred>& d) { return d == task; }),
m_deferred.end());
}
}
guard.unlock();
for (auto&& p : cbs) {
p.first(p.second);
}
}
bool taskqueue::purge(const string& id) {
std::lock_guard<std::mutex> guard(m_lock);
return m_deferred.erase(std::remove_if(m_deferred.begin(), m_deferred.end(),
[id](const unique_ptr<deferred>& d) { return d->id == id; }),
m_deferred.end()) == m_deferred.end();
}
bool taskqueue::exist(const string& id) {
std::lock_guard<std::mutex> guard(m_lock);
for (const auto& task : m_deferred) {
if (task->id == id) {
return true;
}
}
return false;
}
POLYBAR_NS_END

View File

@ -127,7 +127,8 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS;
}
if (cli->has("print-wmname")) {
printf("%s\n", bar::make(true)->settings().wmname.c_str());
eventloop loop{};
printf("%s\n", bar::make(loop, true)->settings().wmname.c_str());
return EXIT_SUCCESS;
}

View File

@ -1,10 +1,9 @@
#include "utils/env.hpp"
#include <cstdlib>
#include <cstring>
#include <thread>
#include <utility>
#include "utils/env.hpp"
POLYBAR_NS
namespace env_util {
@ -17,6 +16,6 @@ namespace env_util {
const char* value{std::getenv(var.c_str())};
return value != nullptr ? value : move(fallback);
}
}
} // namespace env_util
POLYBAR_NS_END