diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 06d3afb3..aacd029a 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -1,17 +1,22 @@ #pragma once +#include #include #include "common.hpp" #include "components/types.hpp" #include "errors.hpp" #include "events/signal_fwd.hpp" +#include "events/signal_receiver.hpp" #include "x11/events.hpp" #include "x11/types.hpp" #include "x11/window.hpp" POLYBAR_NS +namespace chrono = std::chrono; +using namespace std::chrono_literals; + // fwd class connection; class logger; @@ -22,8 +27,12 @@ class signal_emitter; class taskqueue; class tray_manager; +namespace sig_ui = signals::ui; +namespace sig_ev = signals::eventqueue; + class bar : public xpp::event::sink { + evt::leave_notify, evt::destroy_notify, evt::client_message>, + public signal_receiver { public: using make_type = unique_ptr; static make_type make(); @@ -52,6 +61,11 @@ class bar : public xpp::event::sink { public: - struct deferred : non_copyable_mixin { + struct deferred { + using clock = chrono::high_resolution_clock; using duration = chrono::milliseconds; - using timepoint = chrono::time_point; - using callback = function; + using timepoint = chrono::time_point; + using callback = function; - explicit deferred(string&& id, timepoint&& tp, callback&& fn) - : id(forward(id)), when(forward(tp)), func(forward(fn)) {} + 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 timepoint when; const callback func; + timepoint now; + duration wait; + size_t count; }; public: @@ -36,10 +39,13 @@ class taskqueue : non_copyable_mixin { explicit taskqueue(); ~taskqueue(); - void defer(string&& id, deferred::duration&& ms, deferred::callback&& fn); - void defer_unique(string&& id, deferred::duration&& ms, deferred::callback&& fn); + 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 has_deferred(string&& id); + bool exist(const string& id); + bool purge(const string& id); protected: void tick(); diff --git a/include/components/types.hpp b/include/components/types.hpp index 6a0f5b60..9219deb7 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -151,6 +151,12 @@ 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{0, 0, size.w, size.h}; diff --git a/include/events/signal.hpp b/include/events/signal.hpp index b8ba8380..c6661212 100644 --- a/include/events/signal.hpp +++ b/include/events/signal.hpp @@ -95,7 +95,7 @@ namespace signals { DEFINE_VALUE_SIGNAL(1, process_quit, event); DEFINE_VALUE_SIGNAL(2, process_update, event); DEFINE_VALUE_SIGNAL(3, process_input, string); - DEFINE_VALUE_SIGNAL(4, process_check, event); + DEFINE_SIGNAL(4, process_check); } namespace ipc { @@ -105,9 +105,12 @@ namespace signals { } namespace ui { - DEFINE_VALUE_SIGNAL(50, button_press, string); - DEFINE_VALUE_SIGNAL(51, visibility_change, bool); - DEFINE_VALUE_SIGNAL(52, dim_window, double); + DEFINE_SIGNAL(50, tick); + DEFINE_VALUE_SIGNAL(51, button_press, string); + DEFINE_VALUE_SIGNAL(52, visibility_change, bool); + DEFINE_VALUE_SIGNAL(53, dim_window, double); + DEFINE_SIGNAL(54, shade_window); + DEFINE_SIGNAL(55, unshade_window); } namespace parser { diff --git a/include/events/signal_fwd.hpp b/include/events/signal_fwd.hpp index ca628bef..39d3835b 100644 --- a/include/events/signal_fwd.hpp +++ b/include/events/signal_fwd.hpp @@ -17,9 +17,12 @@ namespace signals { struct process_action; } namespace ui { + struct tick; struct button_press; struct visibility_change; struct dim_window; + struct shade_window; + struct unshade_window; } namespace parser { struct change_background; diff --git a/src/components/bar.cpp b/src/components/bar.cpp index d008a40b..1d4ee4cd 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -283,6 +283,8 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const m_tray->setup(static_cast(m_opts)); broadcast_visibility(); + + m_sig.attach(this); } /** @@ -291,6 +293,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const bar::~bar() { std::lock_guard guard(m_mutex); m_connection.detach_sink(this, SINK_PRIORITY_BAR); + m_sig.detach(this); } /** @@ -313,6 +316,10 @@ void bar::parse(const string& data, bool force) { std::lock_guard guard(m_mutex, std::adopt_lock); + if (m_opts.shaded) { + return m_log.trace("bar: Ignoring update (shaded)"); + } + if (data == m_lastinput && !force) { return; } @@ -449,10 +456,7 @@ void bar::broadcast_visibility() { * Event handler for XCB_DESTROY_NOTIFY events */ void bar::handle(const evt::client_message& evt) { - if (evt->type != WM_PROTOCOLS || evt->data.data32[0] != WM_DELETE_WINDOW) { - return; - } - if (evt->window == m_opts.window) { + if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW && evt->window == m_opts.window) { m_log.err("Bar window has been destroyed, shutting down..."); m_connection.disconnect(); } @@ -474,11 +478,18 @@ void bar::handle(const evt::destroy_notify& evt) { * _NET_WM_WINDOW_OPACITY atom value */ void bar::handle(const evt::enter_notify&) { +#if DEBUG + if (m_opts.origin == edge::TOP) { + m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(sig_ui::unshade_window{}); }); + return; + } +#endif + if (m_opts.dimmed) { - window win{m_connection, m_opts.window}; - wm_util::set_wm_window_opacity(m_connection, m_opts.window, 1.0 * 0xFFFFFFFF); - m_sig.emit(dim_window{1.0}); - m_opts.dimmed = false; + m_taskqueue->defer_unique("window-dim", 25ms, [&](size_t) { + m_opts.dimmed = false; + m_sig.emit(dim_window{1.0}); + }); } } @@ -489,11 +500,18 @@ void bar::handle(const evt::enter_notify&) { * _NET_WM_WINDOW_OPACITY atom value */ void bar::handle(const evt::leave_notify&) { +#if DEBUG + if (m_opts.origin == edge::TOP) { + m_taskqueue->defer_unique("window-hover", 25ms, [&](size_t) { m_sig.emit(sig_ui::shade_window{}); }); + return; + } +#endif + if (!m_opts.dimmed) { - window win{m_connection, m_opts.window}; - wm_util::set_wm_window_opacity(m_connection, m_opts.window, m_opts.dimvalue * 0xFFFFFFFF); - m_sig.emit(dim_window{double{m_opts.dimvalue}}); - m_opts.dimmed = true; + m_taskqueue->defer_unique("window-dim", 3s, [&](size_t) { + m_opts.dimmed = true; + m_sig.emit(dim_window{double{m_opts.dimvalue}}); + }); } } @@ -518,7 +536,7 @@ void bar::handle(const evt::button_press& evt) { m_buttonpress_btn = static_cast(evt->detail); m_buttonpress_pos = evt->event_x; - const auto deferred_fn = [&] { + const auto deferred_fn = [&](size_t) { for (auto&& action : m_renderer->get_actions()) { if (action.button == m_buttonpress_btn && !action.active && action.test(m_buttonpress_pos)) { m_log.trace("Found matching input area"); @@ -537,13 +555,13 @@ void bar::handle(const evt::button_press& evt) { }; const auto check_double = [&](string&& id, mousebtn&& btn) { - if (!m_taskqueue->has_deferred(string{id})) { + if (!m_taskqueue->exist(id)) { m_doubleclick.event = evt->time; - m_taskqueue->defer_unique(string{id}, chrono::milliseconds{m_doubleclick.offset}, deferred_fn); + 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 = forward(btn); - m_taskqueue->defer_unique(string{id}, 0ms, deferred_fn); + m_buttonpress_btn = btn; + m_taskqueue->defer_unique(id, 0ms, deferred_fn); } }; @@ -554,7 +572,7 @@ void bar::handle(const evt::button_press& evt) { } else if (evt->detail == static_cast(mousebtn::RIGHT)) { check_double("buttonpress-right", mousebtn::DOUBLE_RIGHT); } else { - deferred_fn(); + deferred_fn(0); } } @@ -597,4 +615,113 @@ void bar::handle(const evt::property_notify& evt) { } } +bool bar::on(const sig_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(m_opts.shade_size.h - m_connection.get_geometry(m_opts.window)->height)}; + double steptime{25.0 / 10.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(sig_ui::tick{}); + } + if (!remaining) { + m_renderer->flush(false); + } + 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 sig_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(m_connection.get_geometry(m_opts.window)->height - m_opts.shade_size.h)}; + double steptime{25.0 / 10.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(sig_ui::tick{}); + } + if (!remaining) { + m_renderer->flush(false); + } + 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 sig_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; + } + + uint32_t mask{0}; + uint32_t values[7]{0}; + xcb_params_configure_window_t params{}; + + if (m_opts.shade_size.h > geom->height) { + XCB_AUX_ADD_PARAM(&mask, ¶ms, height, static_cast(geom->height + m_anim_step)); + params.height = std::max(1U, std::min(params.height, static_cast(m_opts.shade_size.h))); + } else if (m_opts.shade_size.h < geom->height) { + XCB_AUX_ADD_PARAM(&mask, ¶ms, height, static_cast(geom->height - m_anim_step)); + params.height = std::max(1U, std::max(params.height, static_cast(m_opts.shade_size.h))); + } + + if (m_opts.shade_pos.y > geom->y) { + XCB_AUX_ADD_PARAM(&mask, ¶ms, y, static_cast(geom->y + m_anim_step)); + params.y = std::min(params.y, static_cast(m_opts.shade_pos.y)); + } else if (m_opts.shade_pos.y < geom->y) { + XCB_AUX_ADD_PARAM(&mask, ¶ms, y, static_cast(geom->y - m_anim_step)); + params.y = std::max(params.y, static_cast(m_opts.shade_pos.y)); + } + + xutils::pack_values(mask, ¶ms, values); + + m_connection.configure_window(m_opts.window, mask, values); + m_connection.flush(); + + return false; +} + +bool bar::on(const sig_ui::dim_window& sig) { + m_opts.dimmed = *sig.data() != 1.0; + wm_util::set_wm_window_opacity(m_connection, m_opts.window, *sig.data() * 0xFFFFFFFF); + m_connection.flush(); + return false; +} + POLYBAR_NS_END diff --git a/src/components/controller.cpp b/src/components/controller.cpp index 4b880871..db133e53 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -321,14 +321,14 @@ void controller::process_eventqueue() { } } - if (evt.type == static_cast(event_type::INPUT)) { + if (evt.type == static_cast(event_type::UPDATE)) { + m_sig.emit(sig_ev::process_update{make_update_evt(evt.flag)}); + } else if (evt.type == static_cast(event_type::INPUT)) { process_inputdata(); } else if (evt.type == static_cast(event_type::QUIT)) { m_sig.emit(sig_ev::process_quit{make_quit_evt(evt.flag)}); - } else if (evt.type == static_cast(event_type::UPDATE)) { - m_sig.emit(sig_ev::process_update{make_update_evt(evt.flag)}); } else if (evt.type == static_cast(event_type::CHECK)) { - m_sig.emit(sig_ev::process_check{make_check_evt()}); + m_sig.emit(sig_ev::process_check{}); } else { m_log.warn("Unknown event type for enqueued event (%d)", evt.type); } diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp index 98d070d1..2604249e 100644 --- a/src/components/renderer.cpp +++ b/src/components/renderer.cpp @@ -254,6 +254,22 @@ void renderer::flush(bool clear) { m_cleared = clear_area; } +#if DEBUG + if (m_bar.shaded && m_bar.origin == edge::TOP) { + m_log.trace("renderer: copy pixmap (shaded=1, clear=%i, geom=%dx%d+%d+%d)", clear, r.width, r.height, r.x, r.y); + auto geom = m_connection.get_geometry(m_window); + auto x1 = 0; + auto y1 = r.height - m_bar.shade_size.h - r.y - geom->height; + auto x2 = r.x; + auto y2 = r.y; + auto w = r.width; + auto h = r.height - m_bar.shade_size.h + geom->height; + m_connection.copy_area(m_pixmap, m_window, m_gcontexts.at(gc::FG), x1, y1, x2, y2, w, h); + m_connection.flush(); + return; + } +#endif + m_log.trace("renderer: copy pixmap (clear=%i, geom=%dx%d+%d+%d)", clear, r.width, r.height, r.x, r.y); m_connection.copy_area(m_pixmap, m_window, m_gcontexts.at(gc::FG), 0, 0, r.x, r.y, r.width, r.height); diff --git a/src/components/taskqueue.cpp b/src/components/taskqueue.cpp index 93817260..b8418797 100644 --- a/src/components/taskqueue.cpp +++ b/src/components/taskqueue.cpp @@ -17,11 +17,12 @@ taskqueue::taskqueue() { if (m_deferred.empty()) { m_hold.wait(guard); } else { - auto now = deferred::timepoint::clock::now(); - auto wait = m_deferred.front()->when; + auto now = deferred::clock::now(); + auto wait = m_deferred.front()->now + m_deferred.front()->wait; for (auto&& task : m_deferred) { - if (task->when < wait) { - wait = task->when; + auto when = task->now + task->wait; + if (when < wait) { + wait = move(when); } } if (wait > now) { @@ -44,41 +45,59 @@ taskqueue::~taskqueue() { } } -void taskqueue::defer(string&& id, deferred::duration&& ms, deferred::callback&& fn) { +void taskqueue::defer( + string id, deferred::duration ms, deferred::callback fn, deferred::duration offset, size_t count) { std::unique_lock guard(m_lock); - auto when = chrono::time_point_cast(deferred::timepoint::clock::now() + ms); - m_deferred.emplace_back(make_unique(forward(id), move(when), forward(fn))); + deferred::timepoint now{chrono::time_point_cast(deferred::clock::now() + move(offset))}; + m_deferred.emplace_back(make_unique(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) { +void taskqueue::defer_unique( + string id, deferred::duration ms, deferred::callback fn, deferred::duration offset, size_t count) { + purge(id); std::unique_lock guard(m_lock); - for (auto it = m_deferred.rbegin(); it != m_deferred.rend(); ++it) { - if ((*it)->id == id) { - m_deferred.erase(std::remove(m_deferred.begin(), m_deferred.end(), (*it)), m_deferred.end()); - } - } - auto when = chrono::time_point_cast(deferred::timepoint::clock::now() + ms); - m_deferred.emplace_back(make_unique(forward(id), move(when), forward(fn))); + deferred::timepoint now{chrono::time_point_cast(deferred::clock::now() + move(offset))}; + m_deferred.emplace_back(make_unique(move(id), move(now), move(ms), move(fn), move(count))); guard.unlock(); m_hold.notify_one(); } void taskqueue::tick() { - if (m_lock.try_lock()) { - std::lock_guard guard(m_lock, std::adopt_lock); - auto now = deferred::timepoint::clock::now(); - for (auto it = m_deferred.rbegin(); it != m_deferred.rend(); ++it) { - if ((*it)->when <= now) { - (*it)->func(); - m_deferred.erase(std::remove(m_deferred.begin(), m_deferred.end(), (*it)), m_deferred.end()); - } + if (!m_lock.try_lock()) { + return; + } + std::unique_lock guard(m_lock, std::adopt_lock); + auto now = chrono::time_point_cast(deferred::clock::now()); + vector> 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& d) { return d == task; }), + m_deferred.end()); } } + guard.unlock(); + for (auto&& p : cbs) { + p.first(p.second); + } } -bool taskqueue::has_deferred(string&& id) { +bool taskqueue::purge(const string& id) { + std::lock_guard guard(m_lock); + return m_deferred.erase(std::remove_if(m_deferred.begin(), m_deferred.end(), + [id](const unique_ptr& d) { return d->id == id; }), + m_deferred.end()) == m_deferred.end(); +} + +bool taskqueue::exist(const string& id) { std::lock_guard guard(m_lock); for (const auto& task : m_deferred) { if (task->id == id) { diff --git a/src/x11/tray_manager.cpp b/src/x11/tray_manager.cpp index a13eb7e7..373eb92a 100644 --- a/src/x11/tray_manager.cpp +++ b/src/x11/tray_manager.cpp @@ -1148,6 +1148,7 @@ bool tray_manager::on(const visibility_change& evt) { bool tray_manager::on(const dim_window& evt) { if (m_activated) { wm_util::set_wm_window_opacity(m_connection, m_tray, *evt.data() * 0xFFFFFFFF); + m_connection.flush(); } // let the event bubble return false; diff --git a/src/x11/wm.cpp b/src/x11/wm.cpp index 430752b7..4142f159 100644 --- a/src/x11/wm.cpp +++ b/src/x11/wm.cpp @@ -42,7 +42,6 @@ namespace wm_util { xcb_intern_atom_reply(conn, xcb_intern_atom(conn, false, 22, "_NET_WM_WINDOW_OPACITY"), nullptr)}; if (reply) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, reply->atom, XCB_ATOM_CARDINAL, 32, 1, &values); - xcb_flush(conn); free(reply); } }