wip: Window autohide

This commit is contained in:
Michael Carlberg 2016-12-21 04:50:43 +01:00
parent 3aa7c3b106
commit 81e6fb062f
11 changed files with 257 additions and 61 deletions

View File

@ -1,17 +1,22 @@
#pragma once
#include <atomic>
#include <mutex>
#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::button_press, evt::expose, evt::property_notify, evt::enter_notify,
evt::leave_notify, evt::destroy_notify, evt::client_message> {
evt::leave_notify, evt::destroy_notify, evt::client_message>,
public signal_receiver<SIGN_PRIORITY_BAR, sig_ui::tick, sig_ui::shade_window, sig_ui::unshade_window, sig_ui::dim_window> {
public:
using make_type = unique_ptr<bar>;
static make_type make();
@ -52,6 +61,11 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
void handle(const evt::expose& evt);
void handle(const evt::property_notify& evt);
bool on(const sig_ui::unshade_window&);
bool on(const sig_ui::shade_window&);
bool on(const sig_ui::tick&);
bool on(const sig_ui::dim_window&);
private:
connection& m_connection;
signal_emitter& m_sig;
@ -73,6 +87,8 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
event_timer m_buttonpress{0L, 5L};
event_timer m_doubleclick{0L, 150L};
double m_anim_step{0};
};
POLYBAR_NS_END

View File

@ -16,17 +16,20 @@ using namespace std::chrono_literals;
class taskqueue : non_copyable_mixin<taskqueue> {
public:
struct deferred : non_copyable_mixin<deferred> {
struct deferred {
using clock = chrono::high_resolution_clock;
using duration = chrono::milliseconds;
using timepoint = chrono::time_point<chrono::high_resolution_clock, duration>;
using callback = function<void()>;
using timepoint = chrono::time_point<clock, duration>;
using callback = function<void(size_t remaining)>;
explicit deferred(string&& id, timepoint&& tp, callback&& fn)
: id(forward<decltype(id)>(id)), when(forward<decltype(tp)>(tp)), func(forward<decltype(fn)>(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<taskqueue> {
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();

View File

@ -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};

View File

@ -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 {

View File

@ -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;

View File

@ -283,6 +283,8 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_tray->setup(static_cast<const bar_settings&>(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<std::mutex> 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<std::mutex> 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<mousebtn>(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<decltype(btn)>(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<uint8_t>(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<double>(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<double>(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, &params, height, static_cast<uint16_t>(geom->height + m_anim_step));
params.height = std::max(1U, std::min(params.height, static_cast<uint32_t>(m_opts.shade_size.h)));
} else if (m_opts.shade_size.h < geom->height) {
XCB_AUX_ADD_PARAM(&mask, &params, height, static_cast<uint16_t>(geom->height - m_anim_step));
params.height = std::max(1U, std::max(params.height, static_cast<uint32_t>(m_opts.shade_size.h)));
}
if (m_opts.shade_pos.y > geom->y) {
XCB_AUX_ADD_PARAM(&mask, &params, y, static_cast<int16_t>(geom->y + m_anim_step));
params.y = std::min(params.y, static_cast<int32_t>(m_opts.shade_pos.y));
} else if (m_opts.shade_pos.y < geom->y) {
XCB_AUX_ADD_PARAM(&mask, &params, y, static_cast<int16_t>(geom->y - m_anim_step));
params.y = std::max(params.y, static_cast<int32_t>(m_opts.shade_pos.y));
}
xutils::pack_values(mask, &params, 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

View File

@ -321,14 +321,14 @@ void controller::process_eventqueue() {
}
}
if (evt.type == static_cast<uint8_t>(event_type::INPUT)) {
if (evt.type == static_cast<uint8_t>(event_type::UPDATE)) {
m_sig.emit(sig_ev::process_update{make_update_evt(evt.flag)});
} else if (evt.type == static_cast<uint8_t>(event_type::INPUT)) {
process_inputdata();
} else if (evt.type == static_cast<uint8_t>(event_type::QUIT)) {
m_sig.emit(sig_ev::process_quit{make_quit_evt(evt.flag)});
} else if (evt.type == static_cast<uint8_t>(event_type::UPDATE)) {
m_sig.emit(sig_ev::process_update{make_update_evt(evt.flag)});
} else if (evt.type == static_cast<uint8_t>(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);
}

View File

@ -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);

View File

@ -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<std::mutex> guard(m_lock);
auto when = chrono::time_point_cast<deferred::duration>(deferred::timepoint::clock::now() + ms);
m_deferred.emplace_back(make_unique<deferred>(forward<decltype(id)>(id), move(when), forward<decltype(fn)>(fn)));
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) {
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);
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::duration>(deferred::timepoint::clock::now() + ms);
m_deferred.emplace_back(make_unique<deferred>(forward<decltype(id)>(id), move(when), forward<decltype(fn)>(fn)));
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()) {
std::lock_guard<std::mutex> 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<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::has_deferred(string&& id) {
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) {

View File

@ -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;

View File

@ -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);
}
}