wip(refactor): Improve signal and event handling

This commit is contained in:
Michael Carlberg 2016-12-05 20:41:00 +01:00
parent d45fd76dcd
commit 08be86fbe1
73 changed files with 2228 additions and 2251 deletions

View File

@ -4,12 +4,8 @@
#define BOOST_DI_CFG_DIAGNOSTICS_LEVEL 2
#endif
#include <boost/di.hpp>
#include <cstring>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "config.hpp"
@ -41,7 +37,6 @@
POLYBAR_NS
namespace di = boost::di;
namespace placeholders = std::placeholders;
using std::string;
@ -58,7 +53,6 @@ using std::make_unique;
using std::make_shared;
using std::make_pair;
using std::array;
using std::map;
using std::vector;
using std::to_string;

View File

@ -2,12 +2,16 @@
#include "common.hpp"
#include "components/config.hpp"
#include "components/screen.hpp"
#include "components/types.hpp"
#include "errors.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp"
#include "utils/concurrency.hpp"
#include "utils/throttle.hpp"
#include "x11/connection.hpp"
#include "x11/events.hpp"
#include "x11/tray_manager.hpp"
#include "x11/types.hpp"
#include "x11/window.hpp"
@ -21,20 +25,16 @@ class renderer;
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify> {
public:
explicit bar(connection& conn, const config& config, const logger& logger, unique_ptr<screen> screen, unique_ptr<tray_manager> tray_manager);
~bar();
explicit bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger,
unique_ptr<screen> screen, unique_ptr<tray_manager> tray_manager);
void bootstrap(bool nodraw = false);
void bootstrap_tray();
void activate_tray();
~bar();
const bar_settings settings() const;
void parse(const string& data, bool force = false);
protected:
void setup_monitor();
void configure_geom();
void restack_window();
void reconfigure_pos();
void reconfigure_struts();
@ -47,22 +47,37 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
private:
connection& m_connection;
signal_emitter& m_sig;
const config& m_conf;
const logger& m_log;
unique_ptr<screen> m_screen;
unique_ptr<tray_manager> m_tray;
unique_ptr<renderer> m_renderer;
xcb_window_t m_window;
bar_settings m_opts;
string m_lastinput;
std::mutex m_mutex;
event_timer m_buttonpress{};
event_timer m_buttonpress{0L, 5L};
};
di::injector<unique_ptr<bar>> configure_bar();
namespace {
/**
* Configure bar controller
*/
inline unique_ptr<bar> make_bar() {
// clang-format off
return factory_util::unique<bar>(
make_connection(),
make_signal_emitter(),
make_confreader(),
make_logger(),
make_screen(),
make_tray_manager());
// clang-format on
}
}
POLYBAR_NS_END

View File

@ -1,5 +1,7 @@
#pragma once
#include <map>
#include "common.hpp"
#include "components/config.hpp"
#include "components/types.hpp"
@ -7,6 +9,8 @@
POLYBAR_NS
using std::map;
#define DEFAULT_SPACING -1
#ifndef BUILDER_SPACE_TOKEN
@ -28,7 +32,7 @@ class builder {
explicit builder(const bar_settings bar) : m_bar(bar) {}
string flush();
void append(string text);
void append(const string& text);
void node(string str, bool add_space = false);
void node(string str, int font_index, bool add_space = false);
void node(const label_t& label, bool add_space = false);

View File

@ -1,7 +1,10 @@
#pragma once
#include <map>
#include "common.hpp"
#include "errors.hpp"
#include "utils/factory.hpp"
POLYBAR_NS
@ -12,7 +15,7 @@ namespace command_line {
class option;
using choices = vector<string>;
using options = vector<option>;
using values = map<string, string>;
using values = std::map<string, string>;
// class definition : option {{{
@ -69,13 +72,8 @@ namespace {
/**
* Configure injection module
*/
template <class T = cliparser>
di::injector<T> configure_cliparser(string scriptname, const clioptions& opts) {
// clang-format off
return di::make_injector(
di::bind<>().to("Usage: " + scriptname + " bar_name [OPTION...]"),
di::bind<>().to(opts));
// clang-format on
inline unique_ptr<cliparser> make_command_line(string scriptname, const clioptions& opts) {
return factory_util::unique<cliparser>("Usage: " + scriptname + " bar_name [OPTION...]", opts);
}
}

View File

@ -268,9 +268,9 @@ namespace {
/**
* Configure injection module
*/
template <typename T = const config&>
di::injector<T> configure_config() {
return di::make_injector(configure_logger(), configure_xresource_manager());
inline const config& make_confreader() {
shared_ptr<config> instance = factory_util::singleton<config>(make_logger(), make_xresource_manager());
return static_cast<config&>(*instance);
}
}

View File

@ -3,11 +3,16 @@
#include <csignal>
#include "common.hpp"
#include "components/bar.hpp"
#include "components/config.hpp"
#include "components/eventloop.hpp"
#include "components/ipc.hpp"
#include "components/logger.hpp"
#include "components/types.hpp"
#include "config.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp"
#include "utils/command.hpp"
#include "utils/inotify.hpp"
#include "x11/connection.hpp"
@ -18,65 +23,46 @@ POLYBAR_NS
// fwd decl {{{
class bar;
struct bar_settings;
// }}}
using watch_t = inotify_util::watch_t;
class controller {
public:
explicit controller(connection& conn, const logger& logger, const config& config, unique_ptr<eventloop> eventloop,
unique_ptr<bar> bar, inotify_util::watch_t& confwatch)
: m_connection(conn)
, m_log(logger)
, m_conf(config)
, m_eventloop(forward<decltype(eventloop)>(eventloop))
, m_bar(forward<decltype(bar)>(bar))
, m_confwatch(confwatch) {}
namespace sig_ev = signals::eventloop;
namespace sig_ui = signals::ui;
namespace sig_ipc = signals::ipc;
class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, sig_ev::process_update, sig_ev::process_input,
sig_ev::process_quit, sig_ui::button_press, sig_ipc::process_action, sig_ipc::process_command,
sig_ipc::process_hook> {
public:
explicit controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config,
unique_ptr<eventloop> eventloop, unique_ptr<bar> bar, unique_ptr<ipc> ipc, watch_t confwatch, bool writeback);
~controller();
void bootstrap(bool writeback = false, bool dump_wmname = false);
void setup();
bool run();
const bar_settings opts() const;
protected:
void install_sigmask();
void uninstall_sigmask();
void install_confwatch();
void uninstall_confwatch();
void wait_for_signal();
void wait_for_xevent();
void wait_for_eventloop();
void wait_for_configwatch();
void bootstrap_modules();
void on_ipc_action(const ipc_action& message);
void on_mouse_event(const string& input);
void on_unrecognized_action(string input);
void on_update(bool force);
private:
enum class thread_role {
EVENT_QUEUE,
EVENT_QUEUE_X,
IPC_LISTENER,
CONF_LISTENER,
};
bool is_thread_joinable(thread_role&& role) {
if (m_threads.find(role) == m_threads.end()) {
return false;
} else {
return m_threads[role].joinable();
}
}
bool on(const sig_ev::process_update& evt);
bool on(const sig_ev::process_input& evt);
bool on(const sig_ev::process_quit& evt);
bool on(const sig_ui::button_press& evt);
bool on(const sig_ipc::process_action& evt);
bool on(const sig_ipc::process_command& evt);
bool on(const sig_ipc::process_hook& evt);
private:
connection& m_connection;
registry m_registry{m_connection};
signal_emitter& m_sig;
const logger& m_log;
const config& m_conf;
unique_ptr<eventloop> m_eventloop;
@ -86,18 +72,37 @@ class controller {
stateflag m_running{false};
stateflag m_reload{false};
stateflag m_waiting{false};
stateflag m_trayactivated{false};
sigset_t m_blockmask;
sigset_t m_waitmask;
map<thread_role, thread> m_threads;
vector<thread> m_threads;
inotify_util::watch_t& m_confwatch;
command_util::command_t m_command;
watch_t m_confwatch;
command_t m_command;
bool m_writeback{false};
};
di::injector<unique_ptr<controller>> configure_controller(watch_t& confwatch);
namespace {
inline unique_ptr<controller> make_controller(watch_t&& confwatch, bool enableipc = false, bool writeback = false) {
unique_ptr<ipc> ipc;
if (enableipc) {
ipc = make_ipc();
}
// clang-format off
return factory_util::unique<controller>(
make_connection(),
make_signal_emitter(),
make_logger(),
make_confreader(),
make_eventloop(),
make_bar(),
move(ipc),
move(confwatch),
writeback);
// clang-format on
}
}
POLYBAR_NS_END

View File

@ -5,13 +5,21 @@
#include "common.hpp"
#include "components/logger.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp"
#include "modules/meta/base.hpp"
POLYBAR_NS
using namespace signals::eventloop;
using module_t = unique_ptr<modules::module_interface>;
using modulemap_t = map<alignment, vector<module_t>>;
class eventloop : public signal_receiver<SIGN_PRIORITY_EVENTLOOP, process_quit, process_input, process_check,
enqueue_event, enqueue_quit, enqueue_update, enqueue_input, enqueue_check> {
public:
enum class event_type : uint8_t {
NONE = 0,
UPDATE,
@ -21,88 +29,83 @@ enum class event_type : uint8_t {
};
struct event {
uint8_t type{0};
char data[256]{'\0'};
uint8_t type{static_cast<uint8_t>(event_type::NONE)};
bool flag{false};
};
struct quit_event {
const uint8_t type{static_cast<uint8_t>(event_type::QUIT)};
bool reload{false};
struct input_data {
char data[EVENT_SIZE];
};
struct update_event {
const uint8_t type{static_cast<uint8_t>(event_type::UPDATE)};
bool force{false};
};
struct input_event {
const uint8_t type{static_cast<uint8_t>(event_type::INPUT)};
char data[256]{'\0'};
};
class eventloop {
public:
/**
* Queue type
*/
using entry_t = event;
using queue_t = moodycamel::BlockingConcurrentQueue<entry_t>;
template <typename EventType>
using queue_t = moodycamel::BlockingConcurrentQueue<EventType>;
using duration_t = chrono::duration<double, std::milli>;
explicit eventloop(const logger& logger, const config& config);
public:
explicit eventloop(signal_emitter& emitter, const logger& logger, const config& config);
~eventloop();
void start();
void wait();
void stop();
bool enqueue(const entry_t& entry);
bool enqueue_delayed(const entry_t& entry);
void set_update_cb(callback<bool>&& cb);
void set_input_db(callback<string>&& cb);
bool enqueue(event&& evt);
bool enqueue(input_data&& evt);
void add_module(const alignment pos, module_t&& module);
const modulemap_t& modules() const;
size_t module_count() const;
static const eventloop::entry_t& make(update_event&& event, bool force = false) {
event.force = force;
return reinterpret_cast<const eventloop::entry_t&>(event);
static auto make_quit_evt(bool reload = false) {
return event{static_cast<uint8_t>(event_type::QUIT), reload};
}
static const eventloop::entry_t& make(quit_event&& event, bool reload = false) {
event.reload = reload;
return reinterpret_cast<const eventloop::entry_t&>(event);
static auto make_update_evt(bool force = false) {
return event{static_cast<uint8_t>(event_type::UPDATE), force};
}
static const eventloop::entry_t& make(input_event&& event, const string& data) {
snprintf(event.data, sizeof(event.data), "%s", data.c_str());
return reinterpret_cast<const eventloop::entry_t&>(event);
static auto make_input_evt() {
return event{static_cast<uint8_t>(event_type::INPUT)};
}
static auto make_input_data(string&& data) {
input_data d{};
memcpy(d.data, &data[0], sizeof(d.data));
return d;
}
static auto make_check_evt() {
return event{static_cast<uint8_t>(event_type::CHECK)};
}
protected:
void process_inputqueue();
void dispatch_modules();
void dispatch_queue_worker();
void dispatch_delayed_worker();
inline bool match_event(entry_t evt, event_type type);
inline bool compare_events(entry_t evt, entry_t evt2);
void forward_event(entry_t evt);
inline bool compare_events(event evt, event evt2);
inline bool compare_events(input_data data, input_data data1);
void on_update(const update_event& evt);
void on_input(const input_event& evt);
void on_check();
void on_quit(const quit_event& evt);
void forward_event(event evt);
bool on(const process_input& evt);
bool on(const process_check& evt);
bool on(const process_quit& evt);
bool on(const enqueue_event& evt);
bool on(const enqueue_quit& evt);
bool on(const enqueue_update& evt);
bool on(const enqueue_input& evt);
bool on(const enqueue_check& evt);
private:
signal_emitter& m_sig;
const logger& m_log;
const config& m_conf;
/**
* @brief Event queue
*/
queue_t m_queue;
queue_t<event> m_queue;
/**
* @brief Event queue
*/
queue_t<input_data> m_inputqueue;
/**
* @brief Loaded modules
@ -114,16 +117,6 @@ class eventloop {
*/
stateflag m_running;
/**
* @brief Callback fired when receiving UPDATE events
*/
callback<bool> m_update_cb;
/**
* @brief Callback fired for unprocessed INPUT events
*/
callback<string> m_unrecognized_input_cb;
/**
* @brief Time to wait for subsequent events
*/
@ -133,45 +126,11 @@ class eventloop {
* @brief Maximum amount of subsequent events to swallow within timeframe
*/
size_t m_swallow_limit{0};
/**
* @brief Time until releasing the lock on the delayed enqueue channel
*/
duration_t m_delayed_time;
/**
* @brief Lock used to control the condition variable
*/
std::mutex m_delayed_lock;
/**
* @brief Condition variable used to manage notifications for delayed enqueues
*/
std::condition_variable m_delayed_cond;
/**
* @brief Pending event on the delayed channel
*/
entry_t m_delayed_entry{0};
/**
* @brief Queue worker thread
*/
thread m_queue_thread;
/**
* @brief Delayed notifier thread
*/
thread m_delayed_thread;
};
namespace {
/**
* Configure injection module
*/
template <typename T = unique_ptr<eventloop>>
di::injector<T> configure_eventloop() {
return di::make_injector(configure_logger(), configure_config());
inline unique_ptr<eventloop> make_eventloop() {
return make_unique<eventloop>(make_signal_emitter(), make_logger(), make_confreader());
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "common.hpp"
POLYBAR_NS
enum class event_type;
struct event;
struct quit_event;
struct update_event;
struct input_event;
class eventloop {
public:
using entry_t = event;
};
POLYBAR_NS_END

View File

@ -2,8 +2,10 @@
#include "common.hpp"
#include "components/logger.hpp"
#include "events/signal_emitter.hpp"
#include "utils/concurrency.hpp"
#include "utils/functional.hpp"
#include "utils/factory.hpp"
POLYBAR_NS
@ -12,15 +14,15 @@ POLYBAR_NS
*/
struct ipc_command {
static constexpr const char* prefix{"cmd:"};
string payload;
char payload[EVENT_SIZE]{'\0'};
};
struct ipc_hook {
static constexpr const char* prefix{"hook:"};
string payload;
char payload[EVENT_SIZE]{'\0'};
};
struct ipc_action {
static constexpr const char* prefix{"action:"};
string payload;
char payload[EVENT_SIZE]{'\0'};
};
/**
@ -32,40 +34,28 @@ struct ipc_action {
*/
class ipc {
public:
explicit ipc(const logger& logger) : m_log(logger) {}
explicit ipc(signal_emitter& emitter, const logger& logger) : m_sig(emitter), m_log(logger) {}
~ipc();
void attach_callback(callback<const ipc_command&>&& cb);
void attach_callback(callback<const ipc_hook&>&& cb);
void attach_callback(callback<const ipc_action&>&& cb);
void receive_messages();
protected:
void parse(const string& payload) const;
void delegate(const ipc_command& message) const;
void delegate(const ipc_hook& message) const;
void delegate(const ipc_action& message) const;
private:
signal_emitter& m_sig;
const logger& m_log;
vector<callback<const ipc_command&>> m_command_callbacks;
vector<callback<const ipc_hook&>> m_hook_callbacks;
vector<callback<const ipc_action&>> m_action_callbacks;
stateflag m_running{false};
string m_fifo;
int m_fd;
stateflag m_running{false};
};
namespace {
/**
* Configure injection module
*/
template <typename T = unique_ptr<ipc>>
di::injector<T> configure_ipc() {
return di::make_injector(configure_logger());
inline unique_ptr<ipc> make_ipc() {
return factory_util::unique<ipc>(make_signal_emitter(), make_logger());
}
}

View File

@ -1,13 +1,15 @@
#pragma once
#include <cstdio>
#include <map>
#include <string>
#include "common.hpp"
#include "utils/factory.hpp"
POLYBAR_NS
enum class loglevel {
enum class loglevel : uint8_t {
NONE = 0,
ERROR,
WARNING,
@ -25,34 +27,26 @@ class logger {
void verbosity(loglevel level);
void verbosity(string level);
/**
* Output a trace message
*/
#ifdef DEBUG_LOGGER // {{{
template <typename... Args>
#ifdef DEBUG_LOGGER
void trace(string message, Args... args) const {
output(loglevel::TRACE, message, args...);
}
#else
#ifdef VERBOSE_TRACELOG
#undef VERBOSE_TRACELOG
#endif
void trace(string, Args...) const {
}
#endif
/**
* Output extra verbose trace message
*/
template <typename... Args>
#ifdef VERBOSE_TRACELOG
void trace_x(string message, Args... args) const {
output(loglevel::TRACE, message, args...);
}
#else
void trace_x(string, Args...) const {
}
template <typename... Args>
void trace_x(Args...) const {}
#endif
#else
template <typename... Args>
void trace(Args...) const {}
template <typename... Args>
void trace_x(Args...) const {}
#endif // }}}
/**
* Output an info message
@ -97,70 +91,56 @@ class logger {
*/
template <typename... Args>
void output(loglevel level, string format, Args... values) const {
if (level > m_level)
if (level > m_level) {
return;
}
auto prefix = m_prefixes.find(level)->second;
auto suffix = m_suffixes.find(level)->second;
// silence the compiler
#if defined(__clang__)
#if defined(__clang__) // {{{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
dprintf(m_fd, (prefix + format + suffix + "\n").c_str(), convert(values)...);
#if defined(__clang__)
#endif // }}}
dprintf(m_fd, (m_prefixes.at(level) + format + m_suffixes.at(level) + "\n").c_str(), convert(values)...);
#if defined(__clang__) // {{{
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#endif // }}}
}
private:
/**
* Logger verbosity level
*/
loglevel m_level = loglevel::TRACE;
loglevel m_level{loglevel::TRACE};
/**
* File descriptor used when writing the log messages
*/
int m_fd = STDERR_FILENO;
int m_fd{STDERR_FILENO};
/**
* Loglevel specific prefixes
*/
// clang-format off
map<loglevel, string> m_prefixes {
{loglevel::TRACE, "polybar|trace "},
{loglevel::INFO, "polybar|info "},
{loglevel::WARNING, "polybar|warn "},
{loglevel::ERROR, "polybar|error "},
};
std::map<loglevel, string> m_prefixes;
/**
* Loglevel specific suffixes
*/
map<loglevel, string> m_suffixes {
{loglevel::TRACE, ""},
{loglevel::INFO, ""},
{loglevel::WARNING, ""},
{loglevel::ERROR, ""},
};
// clang-format on
std::map<loglevel, string> m_suffixes;
};
namespace {
/**
* Configure injection module
*/
template <typename T = const logger&>
di::injector<T> configure_logger(loglevel level = loglevel::NONE) {
auto instance = factory_util::generic_singleton<logger>(level);
return di::make_injector(di::bind<>().to(instance));
namespace {
inline const logger& make_logger(loglevel level = loglevel::NONE) {
auto instance = factory_util::singleton<const logger>(level);
return static_cast<const logger&>(*instance);
}
}

View File

@ -5,6 +5,7 @@
POLYBAR_NS
class signal_emitter;
class logger;
struct bar_settings;
enum class attribute : uint8_t;
@ -17,7 +18,7 @@ DEFINE_CHILD_ERROR(unclosed_actionblocks, parser_error);
class parser {
public:
explicit parser(const logger& logger, const bar_settings& bar);
explicit parser(signal_emitter& emitter, const logger& logger, const bar_settings& bar);
void operator()(string data);
void codeblock(string data);
size_t text(string data);
@ -30,6 +31,7 @@ class parser {
string parse_action_cmd(const string& data);
private:
signal_emitter& m_sig;
const logger& m_log;
const bar_settings& m_bar;
vector<int> m_actions;

View File

@ -1,7 +1,13 @@
#pragma once
#include "common.hpp"
#include "components/logger.hpp"
#include "components/types.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp"
#include "x11/connection.hpp"
#include "x11/fonts.hpp"
#include "x11/types.hpp"
POLYBAR_NS
@ -10,12 +16,17 @@ class connection;
class font_manager;
class logger;
class renderer {
using namespace signals::parser;
class renderer
: public signal_receiver<SIGN_PRIORITY_RENDERER, change_background, change_foreground, change_underline,
change_overline, change_font, change_alignment, offset_pixel, attribute_set, attribute_unset,
attribute_toggle, action_begin, action_end, write_text_ascii, write_text_unicode, write_text_string> {
public:
enum class gc : uint8_t { BG, FG, OL, UL, BT, BB, BL, BR };
explicit renderer(connection& conn, const logger& logger, unique_ptr<font_manager> font_manager,
const bar_settings& bar, const vector<string>& fonts);
explicit renderer(connection& conn, signal_emitter& emitter, const logger& logger,
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts);
~renderer();
xcb_window_t window() const;
@ -52,6 +63,22 @@ class renderer {
int16_t shift_content(int16_t x, const int16_t shift_x);
int16_t shift_content(const int16_t shift_x);
bool on(const change_background& evt);
bool on(const change_foreground& evt);
bool on(const change_underline& evt);
bool on(const change_overline& evt);
bool on(const change_font& evt);
bool on(const change_alignment& evt);
bool on(const offset_pixel& evt);
bool on(const attribute_set& evt);
bool on(const attribute_unset& evt);
bool on(const attribute_toggle& evt);
bool on(const action_begin& evt);
bool on(const action_end& evt);
bool on(const write_text_ascii& evt);
bool on(const write_text_unicode& evt);
bool on(const write_text_string& evt);
#ifdef DEBUG_HINTS
vector<xcb_window_t> m_debughints;
void debug_hints();
@ -65,13 +92,13 @@ class renderer {
private:
connection& m_connection;
signal_emitter& m_sig;
const logger& m_log;
unique_ptr<font_manager> m_fontmanager;
const bar_settings& m_bar;
xcb_rectangle_t m_rect{0, 0, 0U, 0U};
xcb_rectangle_t m_cleared{0, 0, 0U, 0U};
reserve_area m_cleararea{};
@ -95,6 +122,21 @@ class renderer {
xcb_font_t m_gcfont{XCB_NONE};
};
di::injector<unique_ptr<renderer>> configure_renderer(const bar_settings& bar, const vector<string>& fonts);
namespace {
/**
* Configure injection module
*/
inline unique_ptr<renderer> make_renderer(const bar_settings& bar, const vector<string>& fonts) {
// clang-format off
return factory_util::unique<renderer>(
make_connection(),
make_signal_emitter(),
make_logger(),
make_font_manager(),
bar,
fonts);
// clang-format on
}
}
POLYBAR_NS_END

View File

@ -1,7 +1,10 @@
#pragma once
#include "common.hpp"
#include "components/config.hpp"
#include "components/logger.hpp"
#include "components/types.hpp"
#include "events/signal_emitter.hpp"
#include "x11/events.hpp"
#include "x11/window.hpp"
@ -11,10 +14,11 @@ POLYBAR_NS
class config;
class logger;
class connection;
class signal_emitter;
class screen : public xpp::event::sink<evt::randr_screen_change_notify> {
public:
explicit screen(connection& conn, const logger& logger, const config& conf);
explicit screen(connection& conn, signal_emitter& emitter, const logger& logger, const config& conf);
~screen();
struct size size() const {
@ -30,6 +34,7 @@ class screen : public xpp::event::sink<evt::randr_screen_change_notify> {
private:
connection& m_connection;
signal_emitter& m_sig;
const logger& m_log;
const config& m_conf;
@ -37,10 +42,20 @@ class screen : public xpp::event::sink<evt::randr_screen_change_notify> {
xcb_window_t m_proxy{XCB_NONE};
vector<monitor_t> m_monitors;
struct size m_size{0U, 0U};
struct size m_size {
0U, 0U
};
bool m_sigraised{false};
};
di::injector<unique_ptr<screen>> configure_screen();
namespace {
/**
* Configure injection module
*/
inline unique_ptr<screen> make_screen() {
return factory_util::unique<screen>(
make_connection(), make_signal_emitter(), make_logger(), make_confreader());
}
}
POLYBAR_NS_END

View File

@ -1,68 +0,0 @@
#pragma once
#include "common.hpp"
#include "components/eventloop.hpp"
#include "utils/functional.hpp"
POLYBAR_NS
// fwd decl {{{
enum class mousebtn : uint8_t;
enum class syntaxtag : uint8_t;
enum class alignment : uint8_t;
enum class attribute : uint8_t;
// }}}
/**
* @TODO: Allow multiple signal handlers
* @TODO: Encapsulate signals
*/
namespace g_signals {
/**
* Helper used to create no-op "callbacks"
*/
template <typename... T>
static callback<T...> noop = [](T...) {};
/**
* Signals used to communicate with the event loop
*/
namespace event {
extern callback<const eventloop::entry_t&> enqueue;
extern callback<const eventloop::entry_t&> enqueue_delayed;
}
/**
* Signals used to communicate with the bar window
*/
namespace bar {
extern callback<string> action_click;
extern callback<const bool> visibility_change;
}
/**
* Signals used to communicate with the input parser
*/
namespace parser {
extern callback<const uint32_t> background_change;
extern callback<const uint32_t> foreground_change;
extern callback<const uint32_t> underline_change;
extern callback<const uint32_t> overline_change;
extern callback<const alignment> alignment_change;
extern callback<const attribute> attribute_set;
extern callback<const attribute> attribute_unset;
extern callback<const attribute> attribute_toggle;
extern callback<const int8_t> font_change;
extern callback<const int16_t> pixel_offset;
extern callback<const mousebtn, const string> action_block_open;
extern callback<const mousebtn> action_block_close;
extern callback<const uint16_t> ascii_text_write;
extern callback<const uint16_t> unicode_text_write;
extern callback<const char*, const size_t> string_write;
}
}
POLYBAR_NS_END

View File

@ -1,5 +1,7 @@
#pragma once
#include <string>
#include "common.hpp"
#include "x11/color.hpp"
#include "x11/randr.hpp"
@ -80,8 +82,8 @@ struct action {
struct action_block : public action {
alignment align{alignment::NONE};
double start_x{0.0};
double end_x{0.0};
volatile double start_x{0.0};
volatile double end_x{0.0};
bool active{true};
uint16_t width() const {
@ -90,6 +92,11 @@ struct action_block : public action {
};
struct bar_settings {
explicit bar_settings() = default;
bar_settings(const bar_settings& other) = default;
xcb_window_t window;
monitor_t monitor;
edge origin{edge::TOP};
struct size size {
@ -109,7 +116,7 @@ struct bar_settings {
line_settings underline;
line_settings overline;
map<edge, border_settings> borders;
std::unordered_map<edge, border_settings> borders;
uint8_t spacing{1U};
string separator;
@ -128,13 +135,20 @@ struct bar_settings {
rect.x = pos.x;
rect.y = pos.y;
}
if (borders.find(edge::TOP) != borders.end()) {
rect.y += borders.at(edge::TOP).size;
rect.height -= borders.at(edge::TOP).size;
}
if (borders.find(edge::BOTTOM) != borders.end()) {
rect.height -= borders.at(edge::BOTTOM).size;
}
if (borders.find(edge::LEFT) != borders.end()) {
rect.x += borders.at(edge::LEFT).size;
rect.width -= borders.at(edge::LEFT).size;
}
if (borders.find(edge::RIGHT) != borders.end()) {
rect.width -= borders.at(edge::RIGHT).size;
}
return rect;
}
};

View File

@ -29,6 +29,15 @@
#cmakedefine VERBOSE_TRACELOG
#cmakedefine DEBUG_HINTS
static const size_t EVENT_SIZE{64U};
static const int SIGN_PRIORITY_EVENTLOOP{1};
static const int SIGN_PRIORITY_CONTROLLER{2};
static const int SIGN_PRIORITY_SCREEN{3};
static const int SIGN_PRIORITY_BAR{4};
static const int SIGN_PRIORITY_RENDERER{5};
static const int SIGN_PRIORITY_TRAY{6};
static const int SINK_PRIORITY_BAR{1};
static const int SINK_PRIORITY_SCREEN{2};
static const int SINK_PRIORITY_TRAY{3};

View File

@ -1,5 +1,7 @@
#pragma once
#include <map>
#include "common.hpp"
#include "drawtypes/label.hpp"
#include "utils/mixins.hpp"
@ -15,7 +17,7 @@ namespace drawtypes {
operator bool();
protected:
map<string, icon_t> m_icons;
std::map<string, icon_t> m_icons;
};
using iconset_t = shared_ptr<iconset>;

View File

@ -1,6 +1,7 @@
#pragma once
#include <cerrno>
#include <cstring>
#include "common.hpp"

135
include/events/signal.hpp Normal file
View File

@ -0,0 +1,135 @@
#pragma once
#include "common.hpp"
#include "components/eventloop.hpp"
#include "components/ipc.hpp"
#include "components/types.hpp"
#include "utils/functional.hpp"
POLYBAR_NS
// fwd decl {{{
enum class mousebtn : uint8_t;
enum class syntaxtag : uint8_t;
enum class alignment : uint8_t;
enum class attribute : uint8_t;
// }}}
#define DEFINE_SIGNAL(id, name) \
struct name : public detail::base_signal<name, id> { \
using base_type::base_type; \
}
#define DEFINE_VALUE_SIGNAL(id, name, type) \
struct name : public detail::value_signal<name, id, type> { \
using base_type::base_type; \
}
namespace signals {
namespace detail {
// definition : signal {{{
class signal {
public:
explicit signal() = default;
virtual ~signal() {}
virtual size_t size() const = 0;
};
// }}}
// definition : base_signal {{{
template <typename Derived, uint8_t Id>
class base_signal : public signal {
public:
using base_type = base_signal<Derived, Id>;
explicit base_signal() = default;
virtual ~base_signal() {}
static uint8_t id() {
return Id;
}
virtual size_t size() const override {
return sizeof(Derived);
};
};
// }}}
// definition : value_signal {{{
template <typename Derived, uint8_t Id, typename ValueType>
class value_signal : public base_signal<Derived, Id> {
public:
using base_type = value_signal<Derived, Id, ValueType>;
using value_type = ValueType;
explicit value_signal() {}
explicit value_signal(ValueType&& data) {
memcpy(m_data, &data, sizeof(data));
}
virtual ~value_signal() {}
const ValueType* operator()() const {
return reinterpret_cast<const ValueType*>(&m_data);
}
private:
char m_data[sizeof(ValueType)];
};
// }}}
}
namespace eventloop {
using eventloop_t = polybar::eventloop;
DEFINE_VALUE_SIGNAL(1, process_quit, eventloop_t::event);
DEFINE_VALUE_SIGNAL(2, process_update, eventloop_t::event);
DEFINE_VALUE_SIGNAL(3, process_input, eventloop_t::input_data);
DEFINE_VALUE_SIGNAL(4, process_check, eventloop_t::event);
DEFINE_VALUE_SIGNAL(5, enqueue_event, eventloop_t::event);
DEFINE_VALUE_SIGNAL(6, enqueue_quit, eventloop_t::event);
DEFINE_VALUE_SIGNAL(7, enqueue_update, eventloop_t::event);
DEFINE_VALUE_SIGNAL(8, enqueue_input, eventloop_t::input_data);
DEFINE_VALUE_SIGNAL(9, enqueue_check, eventloop_t::event);
}
namespace ipc {
DEFINE_VALUE_SIGNAL(20, process_command, ipc_command);
DEFINE_VALUE_SIGNAL(21, process_hook, ipc_hook);
DEFINE_VALUE_SIGNAL(22, process_action, ipc_action);
}
namespace ui {
DEFINE_VALUE_SIGNAL(50, button_press, string);
DEFINE_VALUE_SIGNAL(51, visibility_change, bool);
}
namespace parser {
DEFINE_VALUE_SIGNAL(70, change_background, uint32_t);
DEFINE_VALUE_SIGNAL(71, change_foreground, uint32_t);
DEFINE_VALUE_SIGNAL(72, change_underline, uint32_t);
DEFINE_VALUE_SIGNAL(73, change_overline, uint32_t);
DEFINE_VALUE_SIGNAL(74, change_font, int8_t);
DEFINE_VALUE_SIGNAL(75, change_alignment, alignment);
DEFINE_VALUE_SIGNAL(76, offset_pixel, int16_t);
DEFINE_VALUE_SIGNAL(77, attribute_set, attribute);
DEFINE_VALUE_SIGNAL(78, attribute_unset, attribute);
DEFINE_VALUE_SIGNAL(79, attribute_toggle, attribute);
DEFINE_VALUE_SIGNAL(80, action_begin, action);
DEFINE_VALUE_SIGNAL(81, action_end, mousebtn);
DEFINE_VALUE_SIGNAL(82, write_text_ascii, uint16_t);
DEFINE_VALUE_SIGNAL(83, write_text_unicode, uint16_t);
DEFINE_VALUE_SIGNAL(84, write_text_string, string);
}
}
POLYBAR_NS_END

View File

@ -0,0 +1,112 @@
#pragma once
#include "common.hpp"
#include "events/signal_receiver.hpp"
#include "utils/factory.hpp"
#include "utils/mixins.hpp"
POLYBAR_NS
/**
* @brief Holds all signal receivers attached to the emitter
*/
extern signal_receivers_t g_signal_receivers;
/**
* Wrapper used to delegate emitted signals
* to attached signal receivers
*/
class signal_emitter {
public:
explicit signal_emitter() = default;
virtual ~signal_emitter() {}
template <typename Signal>
bool emit(const Signal& sig) {
try {
for (auto&& item : g_signal_receivers.at(id<Signal>())) {
if (item.second->on(sig)) {
return true;
}
}
} catch (...) {
}
return false;
}
template <typename Signal, typename Next, typename... Signals>
bool emit(const Signal& sig) const {
return emit<Signal>(sig) || emit<Next, Signals...>(sig);
}
template <uint8_t Priority, typename Signal, typename... Signals>
void attach(signal_receiver<Priority, Signal, Signals...>* s) {
attach<signal_receiver<Priority, Signal, Signals...>, Signal, Signals...>(s);
}
template <uint8_t Priority, typename Signal, typename... Signals>
void detach(signal_receiver<Priority, Signal, Signals...>* s) {
detach<signal_receiver<Priority, Signal, Signals...>, Signal, Signals...>(s);
}
protected:
template <typename Signal>
uint8_t id() const {
return Signal::id();
}
template <typename Receiver, typename Signal>
void attach(Receiver* s) {
attach(s, id<Signal>());
}
template <typename Receiver, typename Signal, typename Next, typename... Signals>
void attach(Receiver* s) {
attach(s, id<Signal>());
attach<Receiver, Next, Signals...>(s);
}
void attach(signal_receiver_interface* s, uint8_t id) {
g_signal_receivers[id].emplace(s->priority(), s);
}
template <typename Receiver, typename Signal>
void detach(Receiver* s) {
detach(s, id<Signal>());
}
template <typename Receiver, typename Signal, typename Next, typename... Signals>
void detach(Receiver* s) {
detach(s, id<Signal>());
detach<Receiver, Next, Signals...>(s);
}
void detach(signal_receiver_interface* d, uint8_t id) {
try {
auto& prio_map = g_signal_receivers.at(id);
const auto& prio_sink_pair = prio_map.equal_range(d->priority());
for (auto it = prio_sink_pair.first; it != prio_sink_pair.second;) {
if (d == it->second) {
it = prio_map.erase(it);
} else {
++it;
}
}
} catch (...) {
}
}
};
namespace {
/**
* Configure injection module
*/
inline signal_emitter& make_signal_emitter() {
auto instance = factory_util::singleton<signal_emitter>();
return static_cast<signal_emitter&>(*instance);
}
}
POLYBAR_NS_END

View File

@ -0,0 +1,48 @@
#pragma once
#include "common.hpp"
POLYBAR_NS
namespace signals {
namespace eventloop {
struct process_quit;
struct process_update;
struct process_input;
struct process_check;
struct enqueue_event;
struct enqueue_quit;
struct enqueue_update;
struct enqueue_input;
struct enqueue_check;
}
namespace ipc {
struct process_command;
struct process_hook;
struct process_action;
}
namespace ui {
struct button_press;
struct visibility_change;
}
namespace parser {
struct change_background;
struct change_foreground;
struct change_underline;
struct change_overline;
struct change_font;
struct change_alignment;
struct offset_pixel;
struct attribute_set;
struct attribute_unset;
struct attribute_toggle;
struct action_begin;
struct action_end;
struct write_text_ascii;
struct write_text_unicode;
struct write_text_string;
}
}
POLYBAR_NS_END

View File

@ -0,0 +1,50 @@
#pragma once
#include <map>
#include <unordered_map>
#include "common.hpp"
POLYBAR_NS
class signal_receiver_interface {
public:
using prio = unsigned int;
using prio_map = std::multimap<prio, signal_receiver_interface*>;
virtual ~signal_receiver_interface() {}
virtual prio priority() const = 0;
template <typename Signal>
bool on(const Signal& signal);
};
template <typename Signal>
class signal_receiver_impl {
public:
virtual ~signal_receiver_impl() {}
virtual bool on(const Signal&) = 0;
};
template <typename Signal>
bool signal_receiver_interface::on(const Signal& s) {
auto event_sink = dynamic_cast<signal_receiver_impl<Signal>*>(this);
if (event_sink != nullptr && event_sink->on(s)) {
return true;
} else {
return false;
}
}
template <uint8_t Priority, typename Signal, typename... Signals>
class signal_receiver : public signal_receiver_interface,
public signal_receiver_impl<Signal>,
public signal_receiver_impl<Signals>... {
public:
prio priority() const {
return Priority;
}
};
using signal_receivers_t = std::unordered_map<uint8_t, signal_receiver_interface::prio_map>;
POLYBAR_NS_END

View File

@ -4,6 +4,7 @@
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <map>
#include "common.hpp"
#include "components/config.hpp"
@ -19,6 +20,7 @@ POLYBAR_NS
namespace chrono = std::chrono;
using namespace std::chrono_literals;
using std::map;
#define DEFAULT_FORMAT "format"

View File

@ -0,0 +1,96 @@
#pragma once
#include "common.hpp"
#include "modules/backlight.hpp"
#include "modules/battery.hpp"
#include "modules/bspwm.hpp"
#include "modules/counter.hpp"
#include "modules/cpu.hpp"
#include "modules/date.hpp"
#include "modules/fs.hpp"
#include "modules/ipc.hpp"
#include "modules/memory.hpp"
#include "modules/menu.hpp"
#include "modules/meta/base.hpp"
#include "modules/script.hpp"
#include "modules/temperature.hpp"
#include "modules/text.hpp"
#include "modules/xbacklight.hpp"
#include "modules/xwindow.hpp"
#include "modules/xworkspaces.hpp"
#if ENABLE_I3
#include "modules/i3.hpp"
#endif
#if ENABLE_MPD
#include "modules/mpd.hpp"
#endif
#if ENABLE_NETWORK
#include "modules/network.hpp"
#endif
#if ENABLE_ALSA
#include "modules/volume.hpp"
#endif
#if WITH_XKB
#include "modules/xkeyboard.hpp"
#endif
#if not(ENABLE_I3 && ENABLE_MPD && ENABLE_NETWORK && ENABLE_ALSA && WITH_XKB)
#include "modules/unsupported.hpp"
#endif
POLYBAR_NS
using namespace modules;
namespace {
template <typename... Args>
module_interface* make_module(string&& name, Args&&... args) {
if (name == "internal/counter") {
return new counter_module(forward<Args>(args)...);
} else if (name == "internal/backlight") {
return new backlight_module(forward<Args>(args)...);
} else if (name == "internal/battery") {
return new battery_module(forward<Args>(args)...);
} else if (name == "internal/bspwm") {
return new bspwm_module(forward<Args>(args)...);
} else if (name == "internal/cpu") {
return new cpu_module(forward<Args>(args)...);
} else if (name == "internal/date") {
return new date_module(forward<Args>(args)...);
} else if (name == "internal/fs") {
return new fs_module(forward<Args>(args)...);
} else if (name == "internal/memory") {
return new memory_module(forward<Args>(args)...);
} else if (name == "internal/i3") {
return new i3_module(forward<Args>(args)...);
} else if (name == "internal/mpd") {
return new mpd_module(forward<Args>(args)...);
} else if (name == "internal/volume") {
return new volume_module(forward<Args>(args)...);
} else if (name == "internal/network") {
return new network_module(forward<Args>(args)...);
} else if (name == "internal/temperature") {
return new temperature_module(forward<Args>(args)...);
} else if (name == "internal/xbacklight") {
return new xbacklight_module(forward<Args>(args)...);
} else if (name == "internal/xkeyboard") {
return new xkeyboard_module(forward<Args>(args)...);
} else if (name == "internal/xwindow") {
return new xwindow_module(forward<Args>(args)...);
} else if (name == "internal/xworkspaces") {
return new xworkspaces_module(forward<Args>(args)...);
} else if (name == "custom/text") {
return new text_module(forward<Args>(args)...);
} else if (name == "custom/script") {
return new script_module(forward<Args>(args)...);
} else if (name == "custom/menu") {
return new menu_module(forward<Args>(args)...);
} else if (name == "custom/ipc") {
return new ipc_module(forward<Args>(args)...);
} else {
throw application_error("Unknown module: " + name);
}
}
}
POLYBAR_NS_END

View File

@ -30,7 +30,7 @@ namespace modules {
static constexpr const char* TAG_OUTPUT{"<output>"};
static constexpr const char* TAG_LABEL{"<label>"};
command_util::command_t m_command;
unique_ptr<command> m_command;
string m_exec;
bool m_tail{false};

View File

@ -25,7 +25,7 @@ namespace modules {
*/
class xbacklight_module : public static_module<xbacklight_module>, public xpp::event::sink<evt::randr_notify> {
public:
xbacklight_module(const bar_settings& bar, const logger& logger, const config& config, string name);
explicit xbacklight_module(const bar_settings& bar, const logger& logger, const config& config, string name);
void setup();
void teardown();
@ -39,24 +39,23 @@ namespace modules {
}
private:
static constexpr auto TAG_LABEL = "<label>";
static constexpr auto TAG_BAR = "<bar>";
static constexpr auto TAG_RAMP = "<ramp>";
static constexpr const char* TAG_LABEL{"<label>"};
static constexpr const char* TAG_BAR{"<bar>"};
static constexpr const char* TAG_RAMP{"<ramp>"};
static constexpr auto EVENT_SCROLLUP = "xbacklight+";
static constexpr auto EVENT_SCROLLDOWN = "xbacklight-";
static constexpr const char* EVENT_SCROLLUP{"xbacklight+"};
static constexpr const char* EVENT_SCROLLDOWN{"xbacklight-"};
connection& m_connection;
monitor_t m_output;
xcb_window_t m_proxy;
event_timer m_randrnotify{};
ramp_t m_ramp;
label_t m_label;
progressbar_t m_progressbar;
bool m_scroll = true;
int m_percentage = 0;
bool m_scroll{true};
int m_percentage{0};
};
}

View File

@ -19,7 +19,7 @@ namespace modules {
class xkeyboard_module : public static_module<xkeyboard_module>,
public xpp::event::sink<evt::xkb_new_keyboard_notify, evt::xkb_state_notify, evt::xkb_indicator_state_notify> {
public:
xkeyboard_module(const bar_settings& bar, const logger& logger, const config& config, string name);
explicit xkeyboard_module(const bar_settings& bar, const logger& logger, const config& config, string name);
void setup();
void teardown();

View File

@ -32,7 +32,7 @@ namespace modules {
*/
class xwindow_module : public static_module<xwindow_module>, public xpp::event::sink<evt::property_notify> {
public:
xwindow_module(const bar_settings&, const logger&, const config&, string);
explicit xwindow_module(const bar_settings&, const logger&, const config&, string);
void setup();
void teardown();

View File

@ -49,7 +49,7 @@ namespace modules {
*/
class xworkspaces_module : public static_module<xworkspaces_module>, public xpp::event::sink<evt::property_notify> {
public:
xworkspaces_module(const bar_settings& bar, const logger& logger, const config& config, string name);
explicit xworkspaces_module(const bar_settings& bar, const logger& logger, const config& config, string name);
void setup();
void teardown();

View File

@ -2,6 +2,7 @@
#include "common.hpp"
#include "components/logger.hpp"
#include "errors.hpp"
#include "utils/concurrency.hpp"
#include "utils/functional.hpp"
@ -73,15 +74,13 @@ namespace command_util {
concurrency_util::spin_lock m_pipelock;
};
using command_t = unique_ptr<command>;
template <typename... Args>
command_t make_command(Args&&... args) {
return make_unique<command>(configure_logger().create<const logger&>(), forward<Args>(args)...);
unique_ptr<command> make_command(Args&&... args) {
return make_unique<command>(make_logger(), forward<Args>(args)...);
}
}
using command = command_util::command;
using command_t = command_util::command_t;
using command_t = unique_ptr<command>;
POLYBAR_NS_END

View File

@ -20,14 +20,19 @@ namespace factory_util {
}
};
template <class InstanceType, class... Deps>
unique_ptr<InstanceType> generic_instance(Deps... deps) {
return make_unique<InstanceType>(deps...);
template <typename T, typename... Deps>
unique_ptr<T> unique(Deps&&... deps) {
return make_unique<T>(forward<Deps>(deps)...);
}
template <class InstanceType, class... Deps>
shared_ptr<InstanceType> generic_singleton(Deps... deps) {
static auto instance = make_shared<InstanceType>(deps...);
template <typename T, typename... Deps>
shared_ptr<T> shared(Deps&&... deps) {
return make_shared<T>(forward<Deps>(deps)...);
}
template <class T, class... Deps>
shared_ptr<T> singleton(Deps&&... deps) {
static auto instance = make_shared<T>(forward<Deps>(deps)...);
return instance;
}
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <map>
#include <X11/Xft/Xft.h>
#include "common.hpp"
@ -26,7 +28,7 @@ class color {
string m_source;
};
extern map<string, class color> g_colorstore;
extern std::map<string, class color> g_colorstore;
extern color g_colorempty;
extern color g_colorblack;
extern color g_colorwhite;

View File

@ -8,9 +8,11 @@
#include <xpp/xpp.hpp>
#include "common.hpp"
#include "utils/factory.hpp"
#include "x11/extensions.hpp"
#include "x11/registry.hpp"
#include "x11/types.hpp"
#include "x11/xutils.hpp"
POLYBAR_NS
@ -18,8 +20,7 @@ using xpp_connection = xpp::connection<XPP_EXTENSION_LIST>;
class connection : public xpp_connection {
public:
explicit connection() {}
explicit connection(xcb_connection_t* conn) : xpp_connection(conn) {}
explicit connection(xcb_connection_t* conn) : connection(conn, 0) {}
explicit connection(xcb_connection_t* conn, int connection_fd)
: xpp_connection(conn), m_connection_fd(connection_fd) {}
@ -54,7 +55,6 @@ class connection : public xpp_connection {
}
void preload_atoms();
void query_extensions();
string id(xcb_window_t w) const;
@ -98,6 +98,14 @@ class connection : public xpp_connection {
int m_connection_fd{0};
};
di::injector<connection&> configure_connection();
namespace {
/**
* Configure injection module
*/
inline connection& make_connection() {
auto instance = factory_util::singleton<connection>(xutils::get_connection(), xutils::get_connection_fd());
return static_cast<connection&>(*instance);
}
}
POLYBAR_NS_END

View File

@ -78,6 +78,6 @@ class font_manager {
XftDraw* m_xftdraw{nullptr};
};
di::injector<unique_ptr<font_manager>> configure_font_manager();
unique_ptr<font_manager> make_font_manager();
POLYBAR_NS_END

View File

@ -0,0 +1,49 @@
#pragma once
#include <xcb/xcb.h>
#include "common.hpp"
#include "utils/concurrency.hpp"
POLYBAR_NS
// fwd declarations
class connection;
struct xembed_data;
class tray_client {
public:
explicit tray_client(connection& conn, xcb_window_t win, uint16_t w, uint16_t h);
tray_client(const tray_client& c) = default;
tray_client& operator=(tray_client& c) = default;
~tray_client();
uint16_t width() const;
uint16_t height() const;
void clear_window() const;
bool match(const xcb_window_t& win) const;
bool mapped() const;
void mapped(bool state);
xcb_window_t window() const;
xembed_data* xembed() const;
void ensure_state() const;
void reconfigure(int16_t x, int16_t y) const;
void configure_notify(int16_t x, int16_t y) const;
protected:
connection& m_connection;
xcb_window_t m_window{0};
shared_ptr<xembed_data> m_xembed;
bool m_mapped{false};
uint16_t m_width;
uint16_t m_height;
}
;
POLYBAR_NS_END

View File

@ -6,8 +6,14 @@
#include "common.hpp"
#include "components/logger.hpp"
#include "components/types.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp"
#include "utils/concurrency.hpp"
#include "x11/atoms.hpp"
#include "x11/connection.hpp"
#include "x11/events.hpp"
#include "x11/graphics.hpp"
#include "x11/tray_client.hpp"
#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
@ -23,24 +29,19 @@ POLYBAR_NS
namespace chrono = std::chrono;
using namespace std::chrono_literals;
using namespace signals::eventloop;
using namespace signals::ui;
// fwd declarations
class connection;
struct xembed_data;
namespace graphics_util {
struct root_pixmap;
}
using root_pixmap = graphics_util::root_pixmap;
// class definition : settings {{{
struct tray_settings {
tray_settings() = default;
tray_settings& operator=(const tray_settings& o) = default;
alignment align{alignment::NONE};
int16_t running{false};
int16_t orig_x{0};
int16_t orig_y{0};
int16_t configured_x{0};
@ -59,55 +60,18 @@ struct tray_settings {
bool detached{false};
};
// }}}
// class definition : tray_client {{{
class tray_client {
public:
explicit tray_client(connection& conn, xcb_window_t win, uint16_t w, uint16_t h);
~tray_client();
uint16_t width() const;
uint16_t height() const;
void clear_window() const;
bool match(const xcb_window_t& win) const;
bool mapped() const;
void mapped(bool state);
xcb_window_t window() const;
xembed_data* xembed() const;
void ensure_state() const;
void reconfigure(int16_t x, int16_t y) const;
void configure_notify(int16_t x, int16_t y) const;
protected:
connection& m_connection;
xcb_window_t m_window{0};
shared_ptr<xembed_data> m_xembed;
stateflag m_mapped{false};
uint16_t m_width;
uint16_t m_height;
};
// }}}
// class definition : tray_manager {{{
class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify, evt::client_message,
evt::configure_request, evt::resize_request, evt::selection_clear, evt::property_notify,
evt::reparent_notify, evt::destroy_notify, evt::map_notify, evt::unmap_notify> {
evt::reparent_notify, evt::destroy_notify, evt::map_notify, evt::unmap_notify>,
public signal_receiver<SIGN_PRIORITY_TRAY, visibility_change> {
public:
explicit tray_manager(connection& conn, const logger& logger);
explicit tray_manager(connection& conn, signal_emitter& emitter, const logger& logger);
~tray_manager();
const tray_settings settings() const;
void bootstrap(tray_settings settings);
void setup(const bar_settings& bar_opts);
void activate();
void activate_delayed(chrono::duration<double, std::milli> delay = 1s);
void deactivate(bool clear_selection = true);
@ -124,8 +88,8 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
void create_window();
void create_bg(bool realloc = false);
void restack_window();
void set_wmhints();
void set_traycolors();
void set_wm_hints();
void set_tray_colors();
void acquire_selection();
void notify_clients();
@ -134,8 +98,6 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
void track_selection_owner(xcb_window_t owner);
void process_docking_request(xcb_window_t win);
void bar_visibility_change(bool visible);
int16_t calculate_x(uint16_t width) const;
int16_t calculate_y() const;
uint16_t calculate_w() const;
@ -144,9 +106,11 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
int16_t calculate_client_x(const xcb_window_t& win);
int16_t calculate_client_y();
bool is_embedded(const xcb_window_t& win) const;
shared_ptr<tray_client> find_client(const xcb_window_t& win) const;
void remove_client(shared_ptr<tray_client>& client, bool reconfigure = true);
int mapped_clients() const;
void remove_client(xcb_window_t window, bool reconfigure = true);
size_t mapped_clients() const;
void handle(const evt::expose& evt);
void handle(const evt::visibility_notify& evt);
@ -160,8 +124,11 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
void handle(const evt::map_notify& evt);
void handle(const evt::unmap_notify& evt);
bool on(const visibility_change& evt);
private:
connection& m_connection;
signal_emitter& m_sig;
const logger& m_log;
vector<shared_ptr<tray_client>> m_clients;
@ -169,7 +136,7 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
xcb_gcontext_t m_gc{0};
xcb_pixmap_t m_pixmap{0};
root_pixmap m_rootpixmap;
graphics_util::root_pixmap m_rootpixmap;
uint16_t m_prevwidth{0};
uint16_t m_prevheight{0};
@ -189,15 +156,12 @@ class tray_manager : public xpp::event::sink<evt::expose, evt::visibility_notify
std::mutex m_mtx;
};
// }}}
namespace {
/**
* Configure injection module
*/
template <class T = unique_ptr<tray_manager>>
di::injector<T> configure_tray_manager() {
return di::make_injector(configure_logger(), configure_connection());
inline unique_ptr<tray_manager> make_tray_manager() {
return factory_util::unique<tray_manager>(make_connection(), make_signal_emitter(), make_logger());
}
}

View File

@ -7,15 +7,12 @@
POLYBAR_NS
namespace wm_util {
void set_wmname(xcb_connection_t* conn, xcb_window_t win, const string& wm_name, const string& wm_class);
void set_wmprotocols(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> flags);
void set_windowtype(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> types);
void set_wmstate(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> states);
void set_wmpid(xcb_connection_t* conn, xcb_window_t win, pid_t pid);
void set_wmdesktop(xcb_connection_t* conn, xcb_window_t win, uint32_t desktop = -1u);
void set_trayorientation(xcb_connection_t* conn, xcb_window_t win, uint32_t orientation);
void set_trayvisual(xcb_connection_t* conn, xcb_window_t win, xcb_visualid_t visual);
void set_wm_name(xcb_connection_t* conn, xcb_window_t win, const string& wm_name, const string& wm_class);
void set_wm_protocols(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> flags);
void set_wm_window_type(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> types);
void set_wm_state(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> states);
void set_wm_pid(xcb_connection_t* conn, xcb_window_t win, pid_t pid);
void set_wm_desktop(xcb_connection_t* conn, xcb_window_t win, uint32_t desktop = -1u);
}
POLYBAR_NS_END

View File

@ -1,5 +1,7 @@
#pragma once
#include <map>
#include "config.hpp"
#if not WITH_XKB
@ -24,6 +26,8 @@
POLYBAR_NS
using std::map;
// fwd
class connection;

View File

@ -22,6 +22,6 @@ class xresource_manager {
XrmDatabase m_db;
};
di::injector<const xresource_manager&> configure_xresource_manager();
const xresource_manager& make_xresource_manager();
POLYBAR_NS_END

View File

@ -16,9 +16,6 @@ namespace xutils {
xcb_connection_t* get_connection();
int get_connection_fd();
uint32_t event_timer_ms(const config& conf, const xcb_button_press_event_t&);
uint32_t event_timer_ms(const config& conf, const xcb_randr_notify_event_t&);
void pack_values(uint32_t mask, const uint32_t* src, uint32_t* dest);
void pack_values(uint32_t mask, const xcb_params_cw_t* src, uint32_t* dest);
void pack_values(uint32_t mask, const xcb_params_gc_t* src, uint32_t* dest);

View File

@ -1,20 +1,24 @@
#include <xcb/xcb_icccm.h>
#include <algorithm>
#include <string>
#include "components/bar.hpp"
#include "components/command_line.hpp"
#include "components/config.hpp"
#include "components/eventloop.hpp"
#include "components/parser.hpp"
#include "components/renderer.hpp"
#include "components/screen.hpp"
#include "components/signals.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "utils/bspwm.hpp"
#include "utils/color.hpp"
#include "utils/math.hpp"
#include "utils/string.hpp"
#include "x11/atoms.hpp"
#include "x11/connection.hpp"
#include "x11/ewmh.hpp"
#include "x11/fonts.hpp"
#include "x11/graphics.hpp"
#include "x11/tray.hpp"
#include "x11/tray_manager.hpp"
#include "x11/wm.hpp"
#include "x11/xutils.hpp"
@ -24,59 +28,51 @@
POLYBAR_NS
namespace ph = std::placeholders;
/**
* Configure injection module
*/
di::injector<unique_ptr<bar>> configure_bar() {
// clang-format off
return di::make_injector(
configure_connection(),
configure_config(),
configure_logger(),
configure_screen(),
configure_tray_manager());
// clang-format on
}
using namespace signals::ui;
using namespace wm_util;
/**
* Construct bar instance
*/
bar::bar(connection& conn, const config& config, const logger& logger, unique_ptr<screen> screen,
unique_ptr<tray_manager> 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
*/
bar::~bar() {
std::lock_guard<std::mutex> guard(m_mutex);
m_connection.detach_sink(this, SINK_PRIORITY_BAR);
m_tray.reset();
}
/**
* Create required components
*
* This is done outside the constructor due to boost::di noexcept
* TODO: Break out all tray handling
*/
void bar::bootstrap(bool nodraw) {
auto bs = m_conf.bar_section();
bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger,
unique_ptr<screen> screen, unique_ptr<tray_manager> tray_manager)
: m_connection(conn)
, m_sig(emitter)
, m_conf(config)
, m_log(logger)
, m_screen(move(screen))
, m_tray(move(tray_manager)) {
string bs{m_conf.bar_section()};
setup_monitor();
// Get available RandR outputs
auto monitor_name = m_conf.get<string>(bs, "monitor", "");
auto monitor_strictmode = m_conf.get<bool>(bs, "monitor-strict", false);
auto monitors = randr_util::get_monitors(m_connection, m_connection.screen()->root, monitor_strictmode);
m_log.trace("bar: Load config values");
{
m_opts.locale = m_conf.get<string>(bs, "locale", "");
m_opts.separator = string_util::trim(m_conf.get<string>(bs, "separator", ""), '"');
m_opts.wmname = m_conf.get<string>(bs, "wm-name", "polybar-" + bs.substr(4) + "_" + m_opts.monitor->name);
m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-");
if (m_conf.get<bool>(bs, "bottom", false)) {
m_opts.origin = edge::BOTTOM;
if (monitors.empty()) {
throw application_error("No monitors found");
} else if (monitor_name.empty()) {
monitor_name = monitors[0]->name;
m_log.warn("No monitor specified, using \"%s\"", monitor_name);
}
// Match against the defined monitor name
for (auto&& monitor : monitors) {
if (monitor->match(monitor_name, monitor_strictmode)) {
m_opts.monitor = move(monitor);
break;
}
}
if (!m_opts.monitor) {
throw application_error("Monitor \"" + monitor_name + "\" not found or disconnected");
}
m_log.trace("bar: Loaded monitor %s (%ix%i+%i+%i)", m_opts.monitor->name, m_opts.monitor->w, m_opts.monitor->h,
m_opts.monitor->x, m_opts.monitor->y);
try {
m_opts.override_redirect = m_conf.get<bool>(bs, "dock");
m_conf.warn_deprecated(bs, "dock", "override-redirect");
@ -84,107 +80,131 @@ void bar::bootstrap(bool nodraw) {
m_opts.override_redirect = m_conf.get<bool>(bs, "override-redirect", m_opts.override_redirect);
}
GET_CONFIG_VALUE(bs, m_opts.spacing, "spacing");
GET_CONFIG_VALUE(bs, m_opts.padding.left, "padding-left");
GET_CONFIG_VALUE(bs, m_opts.padding.right, "padding-right");
GET_CONFIG_VALUE(bs, m_opts.module_margin.left, "module-margin-left");
GET_CONFIG_VALUE(bs, m_opts.module_margin.right, "module-margin-right");
// Build WM_NAME
m_opts.wmname = m_conf.get<string>(bs, "wm-name", "polybar-" + bs.substr(4) + "_" + m_opts.monitor->name);
m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-");
// Load configuration values
m_opts.origin = m_conf.get<bool>(bs, "bottom", false) ? edge::BOTTOM : edge::TOP;
m_opts.spacing = m_conf.get<decltype(m_opts.spacing)>(bs, "spacing", m_opts.spacing);
m_opts.padding.left = m_conf.get<decltype(m_opts.padding.left)>(bs, "padding-left", m_opts.padding.left);
m_opts.padding.right = m_conf.get<decltype(m_opts.padding.right)>(bs, "padding-right", m_opts.padding.right);
m_opts.module_margin.left =
m_conf.get<decltype(m_opts.module_margin.left)>(bs, "module-margin-left", m_opts.module_margin.left);
m_opts.module_margin.right =
m_conf.get<decltype(m_opts.module_margin.right)>(bs, "module-margin-right", m_opts.module_margin.right);
m_opts.separator = string_util::trim(m_conf.get<string>(bs, "separator", ""), '"');
m_opts.locale = m_conf.get<string>(bs, "locale", "");
// Load values used to adjust the struts atom
m_opts.strut.top = m_conf.get<int>("global/wm", "margin-top", 0);
m_opts.strut.bottom = m_conf.get<int>("global/wm", "margin-bottom", 0);
m_buttonpress.offset = xutils::event_timer_ms(m_conf, xcb_button_press_event_t{});
// Load commands used for fallback click handlers
vector<action> actions;
actions.emplace_back(action{mousebtn::LEFT, m_conf.get<string>(bs, "click-left", "")});
actions.emplace_back(action{mousebtn::MIDDLE, m_conf.get<string>(bs, "click-middle", "")});
actions.emplace_back(action{mousebtn::RIGHT, m_conf.get<string>(bs, "click-right", "")});
actions.emplace_back(action{mousebtn::SCROLL_UP, m_conf.get<string>(bs, "scroll-up", "")});
actions.emplace_back(action{mousebtn::SCROLL_DOWN, m_conf.get<string>(bs, "scroll-down", "")});
// Get fallback click handlers
auto click_left = m_conf.get<string>(bs, "click-left", "");
auto click_middle = m_conf.get<string>(bs, "click-middle", "");
auto click_right = m_conf.get<string>(bs, "click-right", "");
auto scroll_up = m_conf.get<string>(bs, "scroll-up", "");
auto scroll_down = m_conf.get<string>(bs, "scroll-down", "");
if (!click_left.empty()) {
m_opts.actions.emplace_back(action{mousebtn::LEFT, move(click_left)});
}
if (!click_middle.empty()) {
m_opts.actions.emplace_back(action{mousebtn::MIDDLE, move(click_middle)});
}
if (!click_right.empty()) {
m_opts.actions.emplace_back(action{mousebtn::RIGHT, move(click_right)});
}
if (!scroll_up.empty()) {
m_opts.actions.emplace_back(action{mousebtn::SCROLL_UP, move(scroll_up)});
}
if (!scroll_down.empty()) {
m_opts.actions.emplace_back(action{mousebtn::SCROLL_DOWN, move(scroll_down)});
for (auto&& act : actions) {
if (!act.command.empty()) {
m_opts.actions.emplace_back(action{act.button, act.command});
}
}
m_log.trace("bar: Load color values");
{
m_opts.background =
color::parse(m_conf.get<string>(bs, "background", color_util::hex<uint16_t>(m_opts.background)));
m_opts.foreground =
color::parse(m_conf.get<string>(bs, "foreground", color_util::hex<uint16_t>(m_opts.foreground)));
auto linecolor = color::parse(m_conf.get<string>(bs, "linecolor", "#f00"));
auto lineheight = m_conf.get<int>(bs, "lineheight", 0);
// Load foreground/background
m_opts.background = color::parse(m_conf.get<string>(bs, "background", color_util::hex<uint16_t>(m_opts.background)));
m_opts.foreground = color::parse(m_conf.get<string>(bs, "foreground", color_util::hex<uint16_t>(m_opts.foreground)));
// Load over-/underline color and size (warn about deprecated params if used)
m_conf.warn_deprecated(bs, "linecolor", "{underline,overline}-color");
m_conf.warn_deprecated(bs, "lineheight", "{underline,overline}-size");
try {
auto linecolor = color::parse(m_conf.get<string>(bs, "linecolor", "#f00"));
auto lineheight = m_conf.get<int>(bs, "lineheight", 0);
m_opts.overline.size = m_conf.get<int16_t>(bs, "overline-size", lineheight);
m_opts.overline.color = color::parse(m_conf.get<string>(bs, "overline-color"));
} catch (const key_error& err) {
m_opts.overline.color = linecolor;
}
try {
m_opts.overline.color = color::parse(m_conf.get<string>(bs, "overline-color", linecolor));
m_opts.underline.size = m_conf.get<uint16_t>(bs, "underline-size", lineheight);
m_opts.underline.color = color::parse(m_conf.get<string>(bs, "underline-color"));
} catch (const key_error& err) {
m_opts.underline.color = linecolor;
}
}
m_opts.underline.color = color::parse(m_conf.get<string>(bs, "underline-color", linecolor));
m_log.trace("bar: Load border values");
{
// Load border settings
auto bsize = m_conf.get<int>(bs, "border-size", 0);
auto bcolor = m_conf.get<string>(bs, "border-color", "#00000000");
m_opts.borders.emplace(edge::TOP, border_settings{});
m_opts.borders[edge::TOP].size = m_conf.get<int>(bs, "border-top", bsize);
m_opts.borders[edge::TOP].color = color::parse(m_conf.get<string>(bs, "border-top-color", bcolor));
m_opts.borders.emplace(edge::BOTTOM, border_settings{});
m_opts.borders[edge::BOTTOM].size = m_conf.get<int>(bs, "border-bottom", bsize);
m_opts.borders[edge::BOTTOM].color = color::parse(m_conf.get<string>(bs, "border-bottom-color", bcolor));
m_opts.borders.emplace(edge::LEFT, border_settings{});
m_opts.borders[edge::LEFT].size = m_conf.get<int>(bs, "border-left", bsize);
m_opts.borders[edge::LEFT].color = color::parse(m_conf.get<string>(bs, "border-left-color", bcolor));
m_opts.borders.emplace(edge::RIGHT, border_settings{});
m_opts.borders[edge::RIGHT].size = m_conf.get<int>(bs, "border-right", bsize);
m_opts.borders[edge::RIGHT].color = color::parse(m_conf.get<string>(bs, "border-right-color", bcolor));
// Load geometry values
auto w = m_conf.get<string>(m_conf.bar_section(), "width", "100%");
auto h = m_conf.get<string>(m_conf.bar_section(), "height", "24");
auto offsetx = m_conf.get<string>(m_conf.bar_section(), "offset-x", "");
auto offsety = m_conf.get<string>(m_conf.bar_section(), "offset-y", "");
if ((m_opts.size.w = atoi(w.c_str())) && w.find('%') != string::npos) {
m_opts.size.w = math_util::percentage_to_value<int>(m_opts.size.w, m_opts.monitor->w);
}
if ((m_opts.size.h = atoi(h.c_str())) && h.find('%') != string::npos) {
m_opts.size.h = math_util::percentage_to_value<int>(m_opts.size.h, m_opts.monitor->h);
}
if ((m_opts.offset.x = atoi(offsetx.c_str())) != 0 && offsetx.find('%') != string::npos) {
m_opts.offset.x = math_util::percentage_to_value<int>(m_opts.offset.x, m_opts.monitor->w);
}
if ((m_opts.offset.y = atoi(offsety.c_str())) != 0 && offsety.find('%') != string::npos) {
m_opts.offset.y = math_util::percentage_to_value<int>(m_opts.offset.y, m_opts.monitor->h);
}
if (nodraw) {
m_log.trace("bar: Abort bootstrap routine (reason: nodraw)");
m_tray.reset();
return;
// Apply offsets
m_opts.pos.x = m_opts.offset.x + m_opts.monitor->x;
m_opts.pos.y = m_opts.offset.y + m_opts.monitor->y;
m_opts.size.h += m_opts.borders[edge::TOP].size;
m_opts.size.h += m_opts.borders[edge::BOTTOM].size;
if (m_opts.origin == edge::BOTTOM) {
m_opts.pos.y = m_opts.monitor->y + m_opts.monitor->h - m_opts.size.h - m_opts.offset.y;
}
if (m_opts.size.w <= 0 || m_opts.size.w > m_opts.monitor->w) {
throw application_error("Resulting bar width is out of bounds");
} else if (m_opts.size.h <= 0 || m_opts.size.h > m_opts.monitor->h) {
throw application_error("Resulting bar height is out of bounds");
}
m_opts.size.w = math_util::cap<int>(m_opts.size.w, 0, m_opts.monitor->w);
m_opts.size.h = math_util::cap<int>(m_opts.size.h, 0, m_opts.monitor->h);
m_opts.center.y = m_opts.size.h;
m_opts.center.y -= m_opts.borders[edge::BOTTOM].size;
m_opts.center.y /= 2;
m_opts.center.y += m_opts.borders[edge::TOP].size;
m_opts.center.x = m_opts.size.w;
m_opts.center.x -= m_opts.borders[edge::RIGHT].size;
m_opts.center.x /= 2;
m_opts.center.x += m_opts.borders[edge::LEFT].size;
m_log.trace("bar: Create renderer");
auto fonts = m_conf.get_list<string>(m_conf.bar_section(), "font", {});
m_renderer = make_renderer(m_opts, move(fonts));
m_log.trace("bar: Attaching sink to registry");
m_connection.attach_sink(this, SINK_PRIORITY_BAR);
configure_geom();
m_renderer = configure_renderer(m_opts, m_conf.get_list<string>(bs, "font", {})).create<unique_ptr<renderer>>();
m_window = m_renderer->window();
m_log.info("Bar window: %s", m_connection.id(m_window));
m_log.info("Bar geometry: %ix%i+%i+%i", m_opts.size.w, m_opts.size.h, m_opts.pos.x, m_opts.pos.y);
m_opts.window = m_renderer->window();
m_log.info("Bar window: %s", m_connection.id(m_opts.window));
restack_window();
m_log.trace("bar: Reconfigure window");
@ -192,190 +212,29 @@ void bar::bootstrap(bool nodraw) {
reconfigure_wm_hints();
m_log.trace("bar: Map window");
m_connection.map_window_checked(m_window);
m_connection.map_window_checked(m_opts.window);
// Reconfigure window position after mapping (required by Openbox)
// Required by Openbox
reconfigure_pos();
m_log.trace("bar: Attach parser signal handlers");
g_signals::parser::background_change = bind(&renderer::set_background, m_renderer.get(), ph::_1);
g_signals::parser::foreground_change = bind(&renderer::set_foreground, m_renderer.get(), ph::_1);
g_signals::parser::underline_change = bind(&renderer::set_underline, m_renderer.get(), ph::_1);
g_signals::parser::overline_change = bind(&renderer::set_overline, m_renderer.get(), ph::_1);
g_signals::parser::pixel_offset = bind(&renderer::fill_shift, m_renderer.get(), ph::_1);
g_signals::parser::alignment_change = bind(&renderer::set_alignment, m_renderer.get(), ph::_1);
g_signals::parser::attribute_set = bind(&renderer::set_attribute, m_renderer.get(), ph::_1, true);
g_signals::parser::attribute_unset = bind(&renderer::set_attribute, m_renderer.get(), ph::_1, false);
g_signals::parser::attribute_toggle = bind(&renderer::toggle_attribute, m_renderer.get(), ph::_1);
g_signals::parser::action_block_open = bind(&renderer::begin_action, m_renderer.get(), ph::_1, ph::_2);
g_signals::parser::action_block_close = bind(&renderer::end_action, m_renderer.get(), ph::_1);
g_signals::parser::font_change = bind(&renderer::set_fontindex, m_renderer.get(), ph::_1);
g_signals::parser::ascii_text_write = bind(&renderer::draw_character, m_renderer.get(), ph::_1);
g_signals::parser::unicode_text_write = bind(&renderer::draw_character, m_renderer.get(), ph::_1);
g_signals::parser::string_write = bind(&renderer::draw_textstring, m_renderer.get(), ph::_1, ph::_2);
try {
m_log.trace("bar: Drawing empty bar");
m_renderer->begin();
m_renderer->fill_background();
m_renderer->end();
} catch (const exception& err) {
throw application_error("Failed to output empty bar window (reason: " + string{err.what()} + ")");
}
}
/**
* Setup tray manager
*/
void bar::bootstrap_tray() {
if (!m_tray) {
return;
}
tray_settings settings;
auto bs = m_conf.bar_section();
auto tray_position = m_conf.get<string>(bs, "tray-position", "");
if (tray_position == "left") {
settings.align = alignment::LEFT;
} else if (tray_position == "right") {
settings.align = alignment::RIGHT;
} else if (tray_position == "center") {
settings.align = alignment::CENTER;
} else {
settings.align = alignment::NONE;
}
if (settings.align == alignment::NONE) {
m_log.warn("Disabling tray manager (reason: disabled in config)");
m_tray.reset();
return;
}
settings.detached = m_conf.get<bool>(bs, "tray-detached", false);
settings.height = m_opts.size.h;
settings.height -= m_opts.borders.at(edge::BOTTOM).size;
settings.height -= m_opts.borders.at(edge::TOP).size;
settings.height_fill = settings.height;
if (settings.height % 2 != 0) {
settings.height--;
}
auto maxsize = m_conf.get<int>(bs, "tray-maxsize", 16);
if (settings.height > maxsize) {
settings.spacing += (settings.height - maxsize) / 2;
settings.height = maxsize;
}
settings.width_max = m_opts.size.w;
settings.width = settings.height;
settings.orig_y = m_opts.pos.y + m_opts.borders.at(edge::TOP).size;
// Apply user-defined scaling
auto scale = m_conf.get<float>(bs, "tray-scale", 1.0f);
settings.width *= scale;
settings.height_fill *= scale;
auto inner_area = m_opts.inner_area(true);
switch (settings.align) {
case alignment::NONE:
break;
case alignment::LEFT:
settings.orig_x = inner_area.x;
break;
case alignment::CENTER:
settings.orig_x = inner_area.x + inner_area.width / 2 - settings.width / 2;
break;
case alignment::RIGHT:
settings.orig_x = inner_area.x + inner_area.width;
break;
}
// Set user-defined background color
if (!(settings.transparent = m_conf.get<bool>(bs, "tray-transparent", settings.transparent))) {
auto bg = m_conf.get<string>(bs, "tray-background", "");
if (bg.length() > 7) {
m_log.warn("Alpha support for the systray is limited. See the wiki for more details.");
}
if (!bg.empty()) {
settings.background = color::parse(bg, g_colorempty);
} else {
settings.background = m_opts.background;
}
if (color_util::alpha_channel(settings.background) == 0) {
settings.transparent = true;
settings.background = 0;
}
}
// Add user-defined padding
settings.spacing += m_conf.get<int>(bs, "tray-padding", 0);
// Add user-defiend offset
auto offset_x_def = m_conf.get<string>(bs, "tray-offset-x", "");
auto offset_y_def = m_conf.get<string>(bs, "tray-offset-y", "");
auto offset_x = atoi(offset_x_def.c_str());
auto offset_y = atoi(offset_y_def.c_str());
if (offset_x != 0 && offset_x_def.find('%') != string::npos) {
if (settings.detached) {
offset_x = math_util::percentage_to_value<int>(offset_x, m_opts.monitor->w);
} else {
offset_x = math_util::percentage_to_value<int>(offset_x, inner_area.width);
}
}
if (offset_y != 0 && offset_y_def.find('%') != string::npos) {
if (settings.detached) {
offset_y = math_util::percentage_to_value<int>(offset_y, m_opts.monitor->h);
} else {
offset_y = math_util::percentage_to_value<int>(offset_y, inner_area.height);
}
}
settings.orig_x += offset_x;
settings.orig_y += offset_y;
// Put the tray next to the bar in the window stack
settings.sibling = m_window;
try {
m_log.trace("bar: Setup tray manager");
m_tray->bootstrap(settings);
} catch (const exception& err) {
m_log.err(err.what());
m_log.warn("Failed to setup tray, disabling...");
m_tray.reset();
}
m_tray->setup(static_cast<const bar_settings&>(m_opts));
broadcast_visibility();
}
/**
* Activate tray manager
* Cleanup signal handlers and destroy the bar window
*/
void bar::activate_tray() {
if (!m_tray) {
return;
}
m_log.trace("bar: Activate tray manager");
try {
broadcast_visibility();
m_tray->activate();
} catch (const exception& err) {
m_log.err(err.what());
m_log.err("Failed to activate tray manager, disabling...");
m_tray.reset();
}
bar::~bar() {
std::lock_guard<std::mutex> guard(m_mutex);
m_connection.detach_sink(this, SINK_PRIORITY_BAR);
}
/**
@ -404,6 +263,7 @@ void bar::parse(const string& data, bool force) {
m_lastinput = data;
m_log.info("Redrawing bar window");
m_renderer->begin();
if (m_tray && !m_tray->settings().detached && m_tray->settings().configured_slots) {
@ -417,8 +277,10 @@ void bar::parse(const string& data, bool force) {
m_renderer->fill_background();
try {
parser parser{m_log, m_opts};
if (!data.empty()) {
parser parser{m_sig, m_log, m_opts};
parser(data);
}
} catch (const parser_error& err) {
m_log.err("Failed to parse contents (reason: %s)", err.what());
}
@ -426,108 +288,6 @@ void bar::parse(const string& data, bool force) {
m_renderer->end();
}
/**
* Configure geometry values
*/
void bar::configure_geom() {
m_log.trace("bar: Configure window geometry");
auto w = m_conf.get<string>(m_conf.bar_section(), "width", "100%");
auto h = m_conf.get<string>(m_conf.bar_section(), "height", "24");
auto offsetx = m_conf.get<string>(m_conf.bar_section(), "offset-x", "");
auto offsety = m_conf.get<string>(m_conf.bar_section(), "offset-y", "");
// look for user-defined width
if ((m_opts.size.w = atoi(w.c_str())) && w.find('%') != string::npos) {
m_opts.size.w = math_util::percentage_to_value<int>(m_opts.size.w, m_opts.monitor->w);
}
// look for user-defined height
if ((m_opts.size.h = atoi(h.c_str())) && h.find('%') != string::npos) {
m_opts.size.h = math_util::percentage_to_value<int>(m_opts.size.h, m_opts.monitor->h);
}
// look for user-defined offset-x
if ((m_opts.offset.x = atoi(offsetx.c_str())) != 0 && offsetx.find('%') != string::npos) {
m_opts.offset.x = math_util::percentage_to_value<int>(m_opts.offset.x, m_opts.monitor->w);
}
// look for user-defined offset-y
if ((m_opts.offset.y = atoi(offsety.c_str())) != 0 && offsety.find('%') != string::npos) {
m_opts.offset.y = math_util::percentage_to_value<int>(m_opts.offset.y, m_opts.monitor->h);
}
// apply offsets
m_opts.pos.x = m_opts.offset.x + m_opts.monitor->x;
m_opts.pos.y = m_opts.offset.y + m_opts.monitor->y;
// apply borders
m_opts.size.h += m_opts.borders[edge::TOP].size;
m_opts.size.h += m_opts.borders[edge::BOTTOM].size;
if (m_opts.origin == edge::BOTTOM) {
m_opts.pos.y = m_opts.monitor->y + m_opts.monitor->h - m_opts.size.h - m_opts.offset.y;
}
if (m_opts.size.w <= 0 || m_opts.size.w > m_opts.monitor->w) {
throw application_error("Resulting bar width is out of bounds");
}
if (m_opts.size.h <= 0 || m_opts.size.h > m_opts.monitor->h) {
throw application_error("Resulting bar height is out of bounds");
}
m_opts.size.w = math_util::cap<int>(m_opts.size.w, 0, m_opts.monitor->w);
m_opts.size.h = math_util::cap<int>(m_opts.size.h, 0, m_opts.monitor->h);
m_opts.center.y = m_opts.size.h;
m_opts.center.y -= m_opts.borders[edge::BOTTOM].size;
m_opts.center.y /= 2;
m_opts.center.y += m_opts.borders[edge::TOP].size;
m_opts.center.x = m_opts.size.w;
m_opts.center.x -= m_opts.borders[edge::RIGHT].size;
m_opts.center.x /= 2;
m_opts.center.x += m_opts.borders[edge::LEFT].size;
m_log.info("Bar geometry: %ix%i+%i+%i", m_opts.size.w, m_opts.size.h, m_opts.pos.x, m_opts.pos.y);
}
/**
* Create monitor object
*/
void bar::setup_monitor() {
m_log.trace("bar: Create monitor from matching X RandR output");
auto strict = m_conf.get<bool>(m_conf.bar_section(), "monitor-strict", false);
auto monitors = randr_util::get_monitors(m_connection, m_screen->root(), strict);
if (monitors.empty()) {
throw application_error("No monitors found");
}
auto name = m_conf.get<string>(m_conf.bar_section(), "monitor", "");
if (name.empty()) {
name = monitors[0]->name;
m_log.warn("No monitor specified, using \"%s\"", name);
}
for (auto&& monitor : monitors) {
if (monitor->match(name, strict)) {
m_opts.monitor = move(monitor);
break;
}
}
if (!m_opts.monitor) {
throw application_error("Monitor \"" + name + "\" not found or disconnected");
}
const auto& m = m_opts.monitor;
m_log.trace("bar: Loaded monitor %s (%ix%i+%i+%i)", m->name, m->w, m->h, m->x, m->y);
}
/**
* Move the bar window above defined sibling
* in the X window stack
@ -544,10 +304,10 @@ void bar::restack_window() {
auto restacked = false;
if (wm_restack == "bspwm") {
restacked = bspwm_util::restack_above_root(m_connection, m_opts.monitor, m_window);
restacked = bspwm_util::restack_above_root(m_connection, m_opts.monitor, m_opts.window);
#if ENABLE_I3
} else if (wm_restack == "i3" && m_opts.override_redirect) {
restacked = i3_util::restack_above_root(m_connection, m_opts.monitor, m_window);
restacked = i3_util::restack_above_root(m_connection, m_opts.monitor, m_opts.window);
} else if (wm_restack == "i3" && !m_opts.override_redirect) {
m_log.warn("Ignoring restack of i3 window (not needed when `override-redirect = false`)");
wm_restack.clear();
@ -568,7 +328,7 @@ void bar::restack_window() {
* Reconfigure window position
*/
void bar::reconfigure_pos() {
window win{m_connection, m_window};
window win{m_connection, m_opts.window};
win.reconfigure_pos(m_opts.pos.x, m_opts.pos.y);
}
@ -592,7 +352,7 @@ void bar::reconfigure_struts() {
h += m_opts.monitor->y;
}
window win{m_connection, m_window};
window win{m_connection, m_opts.window};
win.reconfigure_struts(w, h, m_opts.pos.x, m_opts.origin == edge::BOTTOM);
}
@ -601,42 +361,34 @@ void bar::reconfigure_struts() {
*/
void bar::reconfigure_wm_hints() {
m_log.trace("bar: Set window WM_NAME");
xcb_icccm_set_wm_name(m_connection, m_window, XCB_ATOM_STRING, 8, m_opts.wmname.size(), m_opts.wmname.c_str());
xcb_icccm_set_wm_class(m_connection, m_window, 15, "polybar\0Polybar");
xcb_icccm_set_wm_name(m_connection, m_opts.window, XCB_ATOM_STRING, 8, m_opts.wmname.size(), m_opts.wmname.c_str());
xcb_icccm_set_wm_class(m_connection, m_opts.window, 15, "polybar\0Polybar");
m_log.trace("bar: Set window _NET_WM_WINDOW_TYPE");
wm_util::set_windowtype(m_connection, m_window, {_NET_WM_WINDOW_TYPE_DOCK});
m_log.trace("bar: Set window _NET_Wm_opts.window_TYPE");
set_wm_window_type(m_connection, m_opts.window, {_NET_WM_WINDOW_TYPE_DOCK});
m_log.trace("bar: Set window _NET_WM_STATE");
wm_util::set_wmstate(m_connection, m_window, {_NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE});
set_wm_state(m_connection, m_opts.window, {_NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE});
m_log.trace("bar: Set window _NET_WM_DESKTOP");
wm_util::set_wmdesktop(m_connection, m_window, 0xFFFFFFFF);
set_wm_desktop(m_connection, m_opts.window, 0xFFFFFFFF);
m_log.trace("bar: Set window _NET_WM_PID");
wm_util::set_wmpid(m_connection, m_window, getpid());
set_wm_pid(m_connection, m_opts.window, getpid());
}
/**
* Broadcast current map state
*/
void bar::broadcast_visibility() {
if (!g_signals::bar::visibility_change) {
return m_log.trace("bar: no callback handler set for bar visibility change");
}
try {
auto attr = m_connection.get_window_attributes(m_window);
auto attr = m_connection.get_window_attributes(m_opts.window);
if (attr->map_state == XCB_MAP_STATE_UNVIEWABLE) {
g_signals::bar::visibility_change(false);
m_sig.emit(visibility_change{move(false)});
} else if (attr->map_state == XCB_MAP_STATE_UNMAPPED) {
g_signals::bar::visibility_change(false);
m_sig.emit(visibility_change{move(false)});
} else {
g_signals::bar::visibility_change(true);
}
} catch (const exception& err) {
return;
m_sig.emit(visibility_change{move(true)});
}
}
@ -669,29 +421,17 @@ void bar::handle(const evt::button_press& evt) {
continue;
} else if (action.end_x < evt->event_x) {
continue;
}
} else {
m_log.trace("Found matching input area");
m_log.trace_x("action.command = %s", action.command);
m_log.trace_x("action.button = %i", evt->detail);
m_log.trace_x("action.start_x = %i", action.start_x);
m_log.trace_x("action.end_x = %i", action.end_x);
if (g_signals::bar::action_click) {
g_signals::bar::action_click(action.command);
}
m_sig.emit(button_press{string{action.command}});
return;
}
}
for (auto&& action : m_opts.actions) {
if (action.button == button && !action.command.empty()) {
m_log.trace("Triggering fallback click handler: %s", action.command);
if (g_signals::bar::action_click) {
g_signals::bar::action_click(action.command);
}
m_sig.emit(button_press{string{action.command}});
return;
}
}
@ -705,7 +445,11 @@ void bar::handle(const evt::button_press& evt) {
* Used to redraw the bar
*/
void bar::handle(const evt::expose& evt) {
if (evt->window == m_window && evt->count == 0) {
if (evt->window == m_opts.window && evt->count == 0) {
if (m_tray->settings().running) {
broadcast_visibility();
}
m_log.trace("bar: Received expose event");
m_renderer->flush(false);
}
@ -729,7 +473,7 @@ void bar::handle(const evt::property_notify& evt) {
m_log.trace_x("bar: property_notify(%s)", atom_name);
#endif
if (evt->window == m_window && evt->atom == WM_STATE) {
if (evt->window == m_opts.window && evt->atom == WM_STATE) {
broadcast_visibility();
}
}

View File

@ -54,7 +54,7 @@ string builder::flush() {
/**
* Insert raw text string
*/
void builder::append(string text) {
void builder::append(const string& text) {
m_output += text;
}

View File

@ -4,6 +4,7 @@
#include <utility>
#include "components/config.hpp"
#include "components/logger.hpp"
#include "utils/env.hpp"
#include "utils/file.hpp"

View File

@ -1,114 +1,59 @@
#include <chrono>
#include <mutex>
#include "components/controller.hpp"
#include "common.hpp"
#include "components/bar.hpp"
#include "components/config.hpp"
#include "components/controller.hpp"
#include "components/eventloop.hpp"
#include "components/ipc.hpp"
#include "components/logger.hpp"
#include "components/signals.hpp"
#include "modules/backlight.hpp"
#include "modules/battery.hpp"
#include "modules/bspwm.hpp"
#include "modules/counter.hpp"
#include "modules/cpu.hpp"
#include "modules/date.hpp"
#include "modules/fs.hpp"
#include "modules/ipc.hpp"
#include "modules/memory.hpp"
#include "modules/menu.hpp"
#include "modules/script.hpp"
#include "modules/temperature.hpp"
#include "modules/text.hpp"
#include "modules/xbacklight.hpp"
#include "modules/xwindow.hpp"
#include "modules/xworkspaces.hpp"
#include "events/signal.hpp"
#include "modules/meta/factory.hpp"
#include "utils/factory.hpp"
#include "utils/process.hpp"
#include "utils/string.hpp"
#if ENABLE_I3
#include "modules/i3.hpp"
#endif
#if ENABLE_MPD
#include "modules/mpd.hpp"
#endif
#if ENABLE_NETWORK
#include "modules/network.hpp"
#endif
#if ENABLE_ALSA
#include "modules/volume.hpp"
#endif
#if WITH_XKB
#include "modules/xkeyboard.hpp"
#endif
#if not(ENABLE_I3 && ENABLE_MPD && ENABLE_NETWORK && ENABLE_ALSA && WITH_XKB)
#include "modules/unsupported.hpp"
#endif
#include "x11/xutils.hpp"
POLYBAR_NS
using namespace modules;
namespace chrono = std::chrono;
controller::controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config,
unique_ptr<eventloop> eventloop, unique_ptr<bar> bar, unique_ptr<ipc> ipc, watch_t confwatch, bool writeback)
: m_connection(conn)
, m_sig(emitter)
, m_log(logger)
, m_conf(config)
, m_eventloop(move(eventloop))
, m_bar(move(bar))
, m_ipc(move(ipc))
, m_confwatch(move(confwatch))
, m_writeback(writeback) {}
/**
* Configure injection module
*/
di::injector<unique_ptr<controller>> configure_controller(watch_t& confwatch) {
// clang-format off
return di::make_injector(
di::bind<>().to(confwatch),
configure_connection(),
configure_logger(),
configure_config(),
configure_eventloop(),
configure_bar());
// clang-format on
}
/**
* Stop modules and cleanup X components,
* threads and spawned processes
*/
controller::~controller() {
pthread_sigmask(SIG_UNBLOCK, &m_blockmask, nullptr);
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig, SIGALRM);
pthread_sigmask(SIG_BLOCK, &sig, nullptr);
if (m_command) {
m_log.info("Terminating running shell command");
m_command.reset();
}
if (m_bar) {
m_log.info("Deconstructing bar");
m_bar.reset();
}
if (m_ipc) {
m_log.info("Deconstructing ipc");
m_ipc.reset();
}
if (m_eventloop) {
m_log.info("Deconstructing eventloop");
m_eventloop.reset();
}
if (m_command) {
m_log.info("Terminating running shell command");
m_command.reset();
}
if (m_bar) {
m_log.info("Deconstructing bar");
m_bar.reset();
}
if (m_ipc) {
m_log.info("Deconstructing ipc");
m_ipc.reset();
}
if (!m_writeback) {
m_log.info("Interrupting X event loop");
m_connection.send_dummy_event(m_connection.root());
}
m_log.info("Joining active threads");
for (auto&& th : m_threads) {
if (th.second.joinable()) {
th.second.join();
for (auto&& thread_ : m_threads) {
if (thread_.joinable()) {
thread_.join();
}
}
@ -117,90 +62,82 @@ controller::~controller() {
;
}
m_sig.detach(this);
m_connection.flush();
}
/**
* Initialize components and setup X environment
*/
void controller::bootstrap(bool writeback, bool dump_wmname) {
// Add all signals to the block mask
sigfillset(&m_blockmask);
if (pthread_sigmask(SIG_BLOCK, &m_blockmask, nullptr) == -1) {
throw system_error("Failed to block signals");
}
m_writeback = writeback;
m_log.trace("controller: Initialize X atom cache");
m_connection.preload_atoms();
m_log.trace("controller: Query X extension data");
m_connection.query_extensions();
if (m_conf.get<bool>(m_conf.bar_section(), "enable-ipc", false)) {
m_log.trace("controller: Create IPC handler");
m_ipc = configure_ipc().create<decltype(m_ipc)>();
m_ipc->attach_callback(bind(&controller::on_ipc_action, this, placeholders::_1));
} else {
m_log.info("Inter-process messaging disabled");
}
// Listen for events on the root window to be able to
// break the blocking wait call when cleaning up
m_log.trace("controller: Listen for events on the root window");
const uint32_t value_list[2]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_connection.change_window_attributes_checked(m_connection.root(), XCB_CW_EVENT_MASK, value_list);
m_log.trace("controller: Setup bar");
m_bar->bootstrap(m_writeback || dump_wmname);
m_bar->bootstrap_tray();
if (dump_wmname) {
std::cout << m_bar->settings().wmname << std::endl;
return;
}
m_log.trace("controller: Attach eventloop update callback");
m_eventloop->set_update_cb(bind(&controller::on_update, this, placeholders::_1));
if (!m_writeback) {
m_log.trace("controller: Attach eventloop input callback");
g_signals::bar::action_click = bind(&controller::on_mouse_event, this, placeholders::_1);
m_eventloop->set_input_db(bind(&controller::on_unrecognized_action, this, placeholders::_1));
}
void controller::setup() {
string bs{m_conf.bar_section()};
m_log.trace("controller: Setup user-defined modules");
bootstrap_modules();
for (int i = 0; i < 3; i++) {
alignment align = static_cast<alignment>(i + 1);
string confkey;
if (align == alignment::LEFT) {
confkey = "modules-left";
} else if (align == alignment::CENTER) {
confkey = "modules-center";
} else if (align == alignment::RIGHT) {
confkey = "modules-right";
}
for (auto& module_name : string_util::split(m_conf.get<string>(bs, confkey, ""), ' ')) {
if (module_name.empty()) {
continue;
}
try {
auto type = m_conf.get<string>("module/" + module_name, "type");
if (type == "custom/ipc" && !m_ipc) {
throw application_error("Inter-process messaging needs to be enabled");
}
unique_ptr<module_interface> module{make_module(move(type), m_bar->settings(), m_log, m_conf, module_name)};
module->set_update_cb([&] {
if (m_eventloop && m_running) {
m_sig.emit(enqueue_update{eventloop_t::make_update_evt(false)});
}
});
module->set_stop_cb([&] {
if (m_eventloop && m_running) {
m_sig.emit(enqueue_check{eventloop::make_check_evt()});
}
});
module->setup();
m_eventloop->add_module(align, move(module));
} catch (const std::runtime_error& err) {
m_log.err("Disabling module \"%s\" (reason: %s)", module_name, err.what());
}
}
}
if (!m_eventloop->module_count()) {
throw application_error("No modules created");
}
}
/**
* Launch the controller
*/
bool controller::run() {
assert(!m_connection.connection_has_error());
m_sig.attach(this);
m_log.info("Starting application");
m_running = true;
if (m_confwatch && !m_writeback) {
m_threads[thread_role::CONF_LISTENER] = thread(&controller::wait_for_configwatch, this);
m_threads.emplace_back(thread(&controller::wait_for_configwatch, this));
}
// Start ipc receiver if its enabled
if (m_conf.get<bool>(m_conf.bar_section(), "enable-ipc", false)) {
m_threads[thread_role::IPC_LISTENER] = thread(&ipc::receive_messages, m_ipc.get());
if (m_ipc) {
m_threads.emplace_back(thread(&ipc::receive_messages, m_ipc.get()));
}
// Listen for X events in separate thread
if (!m_writeback) {
m_threads[thread_role::EVENT_QUEUE_X] = thread(&controller::wait_for_xevent, this);
m_threads.emplace_back(thread(&controller::wait_for_xevent, this));
}
// Start event loop
if (m_eventloop) {
m_threads[thread_role::EVENT_QUEUE] = thread(&controller::wait_for_eventloop, this);
m_threads.emplace_back(thread(&controller::wait_for_eventloop, this));
}
m_log.trace("controller: Wait for signal");
@ -226,11 +163,13 @@ bool controller::run() {
m_log.warn("Termination signal received, shutting down...");
m_log.trace("controller: Caught signal %d", caught_signal);
// Signal the eventloop, in case it's still running
m_eventloop->enqueue(eventloop::make_quit_evt(false));
if (m_eventloop) {
m_log.trace("controller: Stopping event loop");
m_eventloop->stop();
}
if (!m_writeback && m_confwatch) {
m_log.trace("controller: Removing config watch");
m_confwatch->remove(true);
@ -239,9 +178,10 @@ bool controller::run() {
return !m_running && !m_reload;
}
/**
* Listen for changes to the config file
*/
const bar_settings controller::opts() const {
return m_bar->settings();
}
void controller::wait_for_configwatch() {
try {
m_log.trace("controller: Attach config watch");
@ -259,19 +199,14 @@ void controller::wait_for_configwatch() {
}
}
/**
* Wait for X events and forward them to
* the event registry
*/
void controller::wait_for_xevent() {
m_log.trace("controller: Listen for X events");
m_connection.flush();
shared_ptr<xcb_generic_event_t> evt;
while (m_running) {
try {
if ((evt = m_connection.wait_for_event()) != nullptr && m_running) {
auto evt = m_connection.wait_for_event();
if (evt && m_running) {
m_connection.dispatch_event(evt);
}
} catch (xpp::connection_error& err) {
@ -279,7 +214,6 @@ void controller::wait_for_xevent() {
} catch (const exception& err) {
m_log.err("Error in X event loop: %s", err.what());
}
if (m_connection.connection_has_error()) {
break;
}
@ -290,14 +224,10 @@ void controller::wait_for_xevent() {
}
}
/**
* Start event loop and wait for it to finish
*/
void controller::wait_for_eventloop() {
m_eventloop->start();
m_eventloop->wait();
this_thread::sleep_for(250ms);
this_thread::sleep_for(std::chrono::milliseconds{250});
if (m_running) {
m_log.trace("controller: eventloop ended, raising SIGALRM");
@ -305,178 +235,16 @@ void controller::wait_for_eventloop() {
}
}
/**
* Create and initialize bar modules
*/
void controller::bootstrap_modules() {
const bar_settings bar{m_bar->settings()};
string bs{m_conf.bar_section()};
for (int i = 0; i < 3; i++) {
alignment align = static_cast<alignment>(i + 1);
string confkey;
switch (align) {
case alignment::LEFT:
confkey = "modules-left";
break;
case alignment::CENTER:
confkey = "modules-center";
break;
case alignment::RIGHT:
confkey = "modules-right";
break;
default:
break;
}
for (auto& module_name : string_util::split(m_conf.get<string>(bs, confkey, ""), ' ')) {
if (module_name.empty()) {
continue;
}
try {
auto type = m_conf.get<string>("module/" + module_name, "type");
module_t module;
if (type == "internal/counter") {
module.reset(new counter_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/backlight") {
module.reset(new backlight_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/battery") {
module.reset(new battery_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/bspwm") {
module.reset(new bspwm_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/cpu") {
module.reset(new cpu_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/date") {
module.reset(new date_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/fs") {
module.reset(new fs_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/memory") {
module.reset(new memory_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/i3") {
module.reset(new i3_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/mpd") {
module.reset(new mpd_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/volume") {
module.reset(new volume_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/network") {
module.reset(new network_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/temperature") {
module.reset(new temperature_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xbacklight") {
module.reset(new xbacklight_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xkeyboard") {
module.reset(new xkeyboard_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xwindow") {
module.reset(new xwindow_module(bar, m_log, m_conf, module_name));
} else if (type == "internal/xworkspaces") {
module.reset(new xworkspaces_module(bar, m_log, m_conf, module_name));
} else if (type == "custom/text") {
module.reset(new text_module(bar, m_log, m_conf, module_name));
} else if (type == "custom/script") {
module.reset(new script_module(bar, m_log, m_conf, module_name));
} else if (type == "custom/menu") {
module.reset(new menu_module(bar, m_log, m_conf, module_name));
} else if (type == "custom/ipc") {
if (!m_ipc) {
throw application_error("Inter-process messaging needs to be enabled");
}
module.reset(new ipc_module(bar, m_log, m_conf, module_name));
m_ipc->attach_callback(
bind(&ipc_module::on_message, static_cast<ipc_module*>(module.get()), placeholders::_1));
} else {
throw application_error("Unknown module: " + module_name);
}
module->set_update_cb(
bind(&eventloop::enqueue, m_eventloop.get(), eventloop::entry_t{static_cast<uint8_t>(event_type::UPDATE)}));
module->set_stop_cb(
bind(&eventloop::enqueue, m_eventloop.get(), eventloop::entry_t{static_cast<uint8_t>(event_type::CHECK)}));
module->setup();
m_eventloop->add_module(align, move(module));
} catch (const std::runtime_error& err) {
m_log.err("Disabling module \"%s\" (reason: %s)", module_name, err.what());
}
}
}
if (!m_eventloop->module_count()) {
throw application_error("No modules created");
}
}
/**
* Callback for received ipc actions
*/
void controller::on_ipc_action(const ipc_action& message) {
if (!m_eventloop) {
return;
}
string action = message.payload.substr(strlen(ipc_action::prefix));
if (action.size() >= sizeof(input_event::data)) {
m_log.warn("Ignoring input event (size)");
} else if (action.empty()) {
m_log.err("Cannot enqueue empty IPC action");
} else {
m_log.info("Enqueuing IPC action: %s", action);
m_eventloop->enqueue(eventloop::make(input_event{}, action));
}
}
/**
* Callback for clicked bar actions
*/
void controller::on_mouse_event(const string& input) {
if (!m_eventloop) {
return;
} else if (input.length() >= sizeof(input_event::data)) {
m_log.warn("Ignoring input event (size)");
} else if (!m_eventloop->enqueue_delayed(eventloop::make(input_event{}, input))) {
m_log.trace_x("controller: Dispatcher busy");
}
}
/**
* Callback for actions not handled internally by a module
*/
void controller::on_unrecognized_action(string input) {
try {
if (m_command) {
m_log.warn("Terminating previous shell command");
m_command->terminate();
}
m_log.info("Executing shell command: %s", input);
m_command = command_util::make_command(input);
m_command->exec();
m_command.reset();
} catch (const application_error& err) {
m_log.err("controller: Error while forwarding input to shell -> %s", err.what());
}
}
/**
* Callback for module content update
*/
void controller::on_update(bool force) {
bool controller::on(const sig_ev::process_update& evt) {
if (!m_bar) {
return;
return false;
}
const bar_settings& bar{m_bar->settings()};
string contents;
string separator{bar.separator};
string padding_left(bar.padding.left, ' ');
string padding_right(bar.padding.right, ' ');
auto margin_left = bar.module_margin.left;
auto margin_right = bar.module_margin.right;
@ -518,9 +286,7 @@ void controller::on_update(bool force) {
if (block_contents.empty()) {
continue;
}
if (is_left) {
} else if (is_left) {
contents += "%{l}";
contents += padding_left;
} else if (is_center) {
@ -542,25 +308,110 @@ void controller::on_update(bool force) {
contents += string_util::replace_all(block_contents, "}%{", " ");
}
if (m_writeback) {
try {
if (!m_writeback) {
m_bar->parse(contents, evt());
} else {
std::cout << contents << std::endl;
return;
}
try {
if (!m_trayactivated) {
m_trayactivated = true;
m_bar->activate_tray();
}
} catch (const exception& err) {
m_log.err("Failed to active tray manager (reason: %s)", err.what());
}
try {
m_bar->parse(contents, force);
} catch (const exception& err) {
m_log.err("Failed to update bar contents (reason: %s)", err.what());
}
return true;
}
bool controller::on(const sig_ev::process_input& evt) {
try {
string input{(*evt()).data};
if (m_command) {
m_log.warn("Terminating previous shell command");
m_command->terminate();
}
m_log.info("Executing shell command: %s", input);
m_command = command_util::make_command(input);
m_command->exec();
m_command.reset();
} catch (const application_error& err) {
m_log.err("controller: Error while forwarding input to shell -> %s", err.what());
}
return true;
}
bool controller::on(const sig_ev::process_quit&) {
kill(getpid(), SIGUSR1);
return false;
}
bool controller::on(const sig_ui::button_press& evt) {
if (!m_eventloop) {
return false;
}
string input{*evt()};
if (input.length() >= sizeof(eventloop::input_data)) {
m_log.warn("Ignoring input event (size)");
} else if (!m_sig.emit(enqueue_input{eventloop::make_input_data(move(input))})) {
m_log.trace_x("controller: Dispatcher busy");
}
return true;
}
bool controller::on(const sig_ipc::process_action& evt) {
ipc_action a{*evt()};
string action{a.payload};
action.erase(0, strlen(ipc_action::prefix));
if (action.size() >= sizeof(eventloop::input_data)) {
m_log.warn("Ignoring input event (size)");
} else if (action.empty()) {
m_log.err("Cannot enqueue empty ipc action");
} else {
m_log.info("Enqueuing ipc action: %s", action);
m_eventloop->enqueue(eventloop::make_input_evt());
}
return true;
}
bool controller::on(const sig_ipc::process_command& evt) {
ipc_command c{*evt()};
string command{c.payload};
command.erase(0, strlen(ipc_command::prefix));
if (command.empty()) {
return false;
}
if (command == "quit") {
m_eventloop->enqueue(eventloop::make_quit_evt(false));
} else if (command == "restart") {
m_eventloop->enqueue(eventloop::make_quit_evt(true));
} else {
m_log.warn("\"%s\" is not a valid ipc command", command);
}
return true;
}
bool controller::on(const sig_ipc::process_hook& evt) {
const ipc_hook hook{*evt()};
for (const auto& block : m_eventloop->modules()) {
for (const auto& module : block.second) {
auto ipc = dynamic_cast<ipc_module*>(module.get());
if (ipc != nullptr) {
ipc->on_message(hook);
}
}
}
return true;
}
POLYBAR_NS_END

View File

@ -1,8 +1,8 @@
#include <csignal>
#include "components/eventloop.hpp"
#include "components/signals.hpp"
#include "components/types.hpp"
#include "events/signal.hpp"
#include "utils/string.hpp"
#include "utils/time.hpp"
#include "x11/color.hpp"
@ -12,31 +12,18 @@ POLYBAR_NS
/**
* Construct eventloop instance
*/
eventloop::eventloop(const logger& logger, const config& config) : m_log(logger), m_conf(config) {
m_delayed_time = duration_t{m_conf.get<double>("settings", "eventloop-delayed-time", 25)};
eventloop::eventloop(signal_emitter& emitter, const logger& logger, const config& config)
: m_sig(emitter), m_log(logger), m_conf(config) {
m_swallow_time = duration_t{m_conf.get<double>("settings", "eventloop-swallow-time", 10)};
m_swallow_limit = m_conf.get<size_t>("settings", "eventloop-swallow", 5U);
g_signals::event::enqueue = bind(&eventloop::enqueue, this, placeholders::_1);
g_signals::event::enqueue_delayed = bind(&eventloop::enqueue_delayed, this, placeholders::_1);
m_sig.attach(this);
}
/**
* Deconstruct eventloop
*/
eventloop::~eventloop() {
g_signals::event::enqueue = g_signals::noop<const eventloop::entry_t&>;
g_signals::event::enqueue_delayed = g_signals::noop<const eventloop::entry_t&>;
m_update_cb = nullptr;
m_unrecognized_input_cb = nullptr;
if (m_delayed_thread.joinable()) {
m_delayed_thread.join();
}
if (m_queue_thread.joinable()) {
m_queue_thread.join();
}
m_sig.detach(this);
for (auto&& block : m_modules) {
for (auto&& module : block.second) {
@ -45,7 +32,7 @@ eventloop::~eventloop() {
module->stop();
module.reset();
});
m_log.trace("eventloop: Deconstruction of %s took %lu microsec.", module_name, cleanup_ms);
m_log.info("eventloop: Deconstruction of %s took %lu microsec.", module_name, cleanup_ms);
}
}
}
@ -56,19 +43,61 @@ eventloop::~eventloop() {
void eventloop::start() {
m_log.info("Starting event loop");
m_running = true;
dispatch_modules();
m_queue_thread = thread(&eventloop::dispatch_queue_worker, this);
m_delayed_thread = thread(&eventloop::dispatch_delayed_worker, this);
while (m_running) {
event evt, next;
input_data data;
m_queue.wait_dequeue(evt);
if (!m_running) {
break;
}
/**
* Wait for worker threads to end
*/
void eventloop::wait() {
if (m_queue_thread.joinable()) {
m_queue_thread.join();
if (evt.type == static_cast<uint8_t>(event_type::INPUT)) {
while (m_running && m_inputqueue.try_dequeue(data)) {
m_sig.emit(process_input{move(data)});
}
} else {
size_t swallowed{0};
while (swallowed++ < m_swallow_limit && m_queue.wait_dequeue_timed(next, m_swallow_time)) {
if (next.type == static_cast<uint8_t>(event_type::QUIT)) {
evt = next;
break;
} else if (next.type == static_cast<uint8_t>(event_type::INPUT)) {
evt = next;
break;
} else if (!compare_events(evt, next)) {
enqueue(move(next));
break;
} else {
m_log.trace_x("eventloop: Swallowing event within timeframe");
evt = next;
}
}
if (evt.type == static_cast<uint8_t>(event_type::INPUT)) {
while (m_inputqueue.try_dequeue(data)) {
m_sig.emit(process_input{move(data)});
}
} else {
forward_event(evt);
}
}
}
m_log.info("Queue worker done");
}
void eventloop::process_inputqueue() {
input_data data{};
while (m_inputqueue.try_dequeue(data)) {
m_sig.emit(process_input{move(data)});
}
}
@ -78,61 +107,33 @@ void eventloop::wait() {
void eventloop::stop() {
m_log.info("Stopping event loop");
m_running = false;
if (m_delayed_thread.joinable()) {
m_delayed_cond.notify_one();
}
enqueue({static_cast<uint8_t>(event_type::QUIT)});
m_sig.emit(enqueue_quit{make_quit_evt(false)});
}
/**
* Enqueue event
*/
bool eventloop::enqueue(const entry_t& entry) {
if (m_queue.enqueue(entry)) {
return true;
}
m_log.warn("Failed to enqueue event (%d)", entry.type);
bool eventloop::enqueue(event&& evt) {
uint8_t type{static_cast<uint8_t>(evt.type)};
if (!m_queue.enqueue(move(evt))) {
m_log.warn("Failed to enqueue event (%d)", static_cast<uint8_t>(type));
return false;
}
/**
* Delay enqueue by given time
*/
bool eventloop::enqueue_delayed(const entry_t& entry) {
if (!m_delayed_lock.try_lock()) {
return false;
}
std::unique_lock<std::mutex> guard(m_delayed_lock, std::adopt_lock);
if (m_delayed_entry.type != 0) {
return false;
}
m_delayed_entry = entry;
if (m_queue.enqueue(entry)) {
return true;
}
m_delayed_entry.type = 0;
/**
* Enqueue input event
*/
bool eventloop::enqueue(input_data&& evt) {
if (!m_inputqueue.enqueue(move(evt))) {
m_log.warn("Failed to enqueue input_data");
return false;
}
/**
* Set callback handler for UPDATE events
*/
void eventloop::set_update_cb(callback<bool>&& cb) {
m_update_cb = forward<decltype(cb)>(cb);
}
/**
* Set callback handler for raw INPUT events
*/
void eventloop::set_input_db(callback<string>&& cb) {
m_unrecognized_input_cb = forward<decltype(cb)>(cb);
return true;
}
/**
@ -184,165 +185,101 @@ void eventloop::dispatch_modules() {
}
}
/**
* Dispatch queue worker thread
*/
void eventloop::dispatch_queue_worker() {
while (m_running) {
entry_t evt, next{static_cast<uint8_t>(event_type::NONE)};
m_queue.wait_dequeue(evt);
if (!m_running) {
break;
}
if (m_delayed_entry.type != 0 && compare_events(evt, m_delayed_entry)) {
m_delayed_cond.notify_one();
}
size_t swallowed{0};
while (swallowed++ < m_swallow_limit && m_queue.wait_dequeue_timed(next, m_swallow_time)) {
if (match_event(next, event_type::QUIT)) {
evt = next;
break;
} else if (!compare_events(evt, next)) {
enqueue(move(next));
break;
}
m_log.trace_x("eventloop: Swallowing event within timeframe");
evt = next;
}
forward_event(evt);
}
m_log.info("Queue worker done");
}
/**
* Dispatch delayed worker thread
*/
void eventloop::dispatch_delayed_worker() {
while (true) {
// wait for notification
while (m_running && m_delayed_entry.type != 0) {
std::unique_lock<std::mutex> guard(m_delayed_lock);
m_delayed_cond.wait(guard, [&] { return m_delayed_entry.type != 0 || !m_running; });
}
if (!m_running) {
break;
}
this_thread::sleep_for(m_delayed_time);
m_delayed_entry.type = 0;
}
m_log.info("Delayed worker done");
}
/**
* Test if event matches given type
*/
inline bool eventloop::match_event(entry_t evt, event_type type) {
return static_cast<uint8_t>(type) == evt.type;
}
/**
* Compare given events
*/
inline bool eventloop::compare_events(entry_t evt, entry_t evt2) {
if (evt.type != evt2.type) {
return false;
} else if (match_event(evt, event_type::INPUT)) {
return evt.data[0] == evt2.data[0] && strncmp(evt.data, evt2.data, strlen(evt.data)) == 0;
inline bool eventloop::compare_events(event evt, event evt2) {
return evt.type != evt2.type;
}
return true;
inline bool eventloop::compare_events(input_data data, input_data data2) {
return data.data[0] == data2.data[0] && strncmp(data.data, data2.data, strlen(data.data)) == 0;
}
/**
* Forward event to handler based on type
*/
void eventloop::forward_event(entry_t evt) {
if (evt.type == static_cast<uint8_t>(event_type::UPDATE)) {
on_update(reinterpret_cast<const update_event&>(evt));
} else if (evt.type == static_cast<uint8_t>(event_type::INPUT)) {
on_input(reinterpret_cast<const input_event&>(evt));
void eventloop::forward_event(event evt) {
if (evt.type == static_cast<uint8_t>(event_type::QUIT)) {
m_sig.emit(process_quit{reinterpret_cast<event&&>(evt)});
} else if (evt.type == static_cast<uint8_t>(event_type::UPDATE)) {
m_sig.emit(process_update{reinterpret_cast<event&&>(evt)});
} else if (evt.type == static_cast<uint8_t>(event_type::CHECK)) {
on_check();
} else if (evt.type == static_cast<uint8_t>(event_type::QUIT)) {
on_quit(reinterpret_cast<const quit_event&>(evt));
m_sig.emit(process_check{reinterpret_cast<event&&>(evt)});
} else {
m_log.warn("Unknown event type for enqueued event (%d)", evt.type);
}
}
/**
* Handler for enqueued UPDATE events
*/
void eventloop::on_update(const update_event& evt) {
m_log.trace("eventloop: Received UPDATE event");
if (m_update_cb) {
m_update_cb(evt.force);
} else {
m_log.warn("No callback to handle update");
}
}
/**
* Handler for enqueued INPUT events
*/
void eventloop::on_input(const input_event& evt) {
m_log.trace("eventloop: Received INPUT event");
bool eventloop::on(const process_input& evt) {
string input{(*evt()).data};
for (auto&& block : m_modules) {
for (auto&& module : block.second) {
if (!module->receive_events()) {
continue;
}
if (module->handle_event(evt.data)) {
return;
if (module->receive_events() && module->handle_event(input)) {
return true;
}
}
}
if (m_unrecognized_input_cb) {
m_unrecognized_input_cb(evt.data);
} else {
m_log.warn("No callback to handle unrecognized input");
}
m_log.warn("Input event \"%s\" was rejected by all modules, passing to shell...", input);
return false;
}
/**
* Handler for enqueued CHECK events
*/
void eventloop::on_check() {
bool eventloop::on(const process_check&) {
for (const auto& block : m_modules) {
for (const auto& module : block.second) {
if (m_running && module->running()) {
return;
return true;
}
}
}
m_log.warn("No running modules...");
stop();
enqueue(make_quit_evt(false));
return true;
}
/**
* Handler for enqueued QUIT events
*/
void eventloop::on_quit(const quit_event& evt) {
m_log.trace("eventloop: Received QUIT event");
bool eventloop::on(const process_quit& evt) {
assert((*evt()).type == static_cast<uint8_t>(event_type::QUIT));
const event quit{static_cast<const event>(*evt())};
m_log.info("Processing QUIT event (reload=%i)", quit.flag);
m_running = false;
if (evt.reload) {
kill(getpid(), SIGUSR1);
return !quit.flag; // break emit chain if reload flag isn't set
}
bool eventloop::on(const enqueue_event& evt) {
m_log.trace("eventloop: enqueuing event (type=%i)", (*evt()).type);
return enqueue(static_cast<event>(*evt()));
}
bool eventloop::on(const enqueue_quit& evt) {
assert((*evt()).type == static_cast<uint8_t>(event_type::QUIT));
if (m_running) {
const event quit{reinterpret_cast<const event&>(*evt())};
m_log.info("Enqueuing QUIT event (reload=%i)", quit.flag);
return enqueue(static_cast<event>(*evt()));
}
return true;
}
bool eventloop::on(const enqueue_update& evt) {
event update{reinterpret_cast<const event&>(*evt())};
assert(update.type == static_cast<uint8_t>(event_type::UPDATE));
m_log.trace("eventloop: enqueuing UPDATE event (force=%i)", update.flag);
return enqueue(move(update));
}
bool eventloop::on(const enqueue_input& evt) {
m_log.trace("eventloop: enqueuing INPUT event");
return enqueue(input_data{(*evt())}) && enqueue(make_input_evt());
}
bool eventloop::on(const enqueue_check& evt) {
event check{reinterpret_cast<const event&>(*evt())};
assert(check.type == static_cast<uint8_t>(event_type::CHECK));
m_log.trace("eventloop: enqueuing CHECK event");
return enqueue(move(check));
}
POLYBAR_NS_END

View File

@ -4,12 +4,16 @@
#include "components/ipc.hpp"
#include "config.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "utils/file.hpp"
#include "utils/io.hpp"
#include "utils/string.hpp"
POLYBAR_NS
using namespace signals::ipc;
/**
* Interrupt the blocked listener and
* remove the file handler
@ -28,27 +32,6 @@ ipc::~ipc() {
}
}
/**
* Register listener callback for ipc_command messages
*/
void ipc::attach_callback(callback<const ipc_command&>&& cb) {
m_command_callbacks.emplace_back(cb);
}
/**
* Register listener callback for ipc_hook messages
*/
void ipc::attach_callback(callback<const ipc_hook&>&& cb) {
m_hook_callbacks.emplace_back(cb);
}
/**
* Register listener callback for ipc_action messages
*/
void ipc::attach_callback(callback<const ipc_action&>&& cb) {
m_action_callbacks.emplace_back(cb);
}
/**
* Start listening for event messages
*/
@ -76,53 +59,20 @@ void ipc::parse(const string& payload) const {
if (payload.empty()) {
return;
} else if (payload.find(ipc_command::prefix) == 0) {
delegate(ipc_command{payload});
ipc_command msg{};
memcpy(msg.payload, &payload[0], payload.size());
m_sig.emit(process_command{move(msg)});
} else if (payload.find(ipc_hook::prefix) == 0) {
delegate(ipc_hook{payload});
ipc_hook msg{};
memcpy(msg.payload, &payload[0], payload.size());
m_sig.emit(process_hook{move(msg)});
} else if (payload.find(ipc_action::prefix) == 0) {
delegate(ipc_action{payload});
ipc_action msg{};
memcpy(msg.payload, &payload[0], payload.size());
m_sig.emit(process_action{move(msg)});
} else {
m_log.warn("Received unknown ipc message: (payload=%s)", payload);
}
}
/**
* Send ipc message to attached listeners
*/
void ipc::delegate(const ipc_command& message) const {
if (!m_command_callbacks.empty()) {
for (auto&& callback : m_command_callbacks) {
callback(message);
}
} else {
m_log.warn("Unhandled message (payload=%s)", message.payload);
}
}
/**
* Send ipc message to attached listeners
*/
void ipc::delegate(const ipc_hook& message) const {
if (!m_hook_callbacks.empty()) {
for (auto&& callback : m_hook_callbacks) {
callback(message);
}
} else {
m_log.warn("Unhandled message (payload=%s)", message.payload);
}
}
/**
* Send ipc message to attached listeners
*/
void ipc::delegate(const ipc_action& message) const {
if (!m_action_callbacks.empty()) {
for (auto&& callback : m_action_callbacks) {
callback(message);
}
} else {
m_log.warn("Unhandled message (payload=%s)", message.payload);
}
}
POLYBAR_NS_END

View File

@ -1,7 +1,5 @@
#include <unistd.h>
#include <utility>
#include "components/logger.hpp"
#include "errors.hpp"
#include "utils/string.hpp"
@ -12,8 +10,8 @@ POLYBAR_NS
* Construct logger
*/
logger::logger(loglevel level) : m_level(level) {
if (isatty(m_fd)) {
// clang-format off
if (isatty(m_fd)) {
m_prefixes[loglevel::TRACE] = "\r\033[0;90m- ";
m_prefixes[loglevel::INFO] = "\r\033[1;32m* \033[0m";
m_prefixes[loglevel::WARNING] = "\r\033[1;33mwarn: \033[0m";
@ -22,8 +20,17 @@ logger::logger(loglevel level) : m_level(level) {
m_suffixes[loglevel::INFO] = "\033[0m";
m_suffixes[loglevel::WARNING] = "\033[0m";
m_suffixes[loglevel::ERROR] = "\033[0m";
// clang-format on
} else {
m_prefixes.emplace(make_pair(loglevel::TRACE, "polybar|trace "));
m_prefixes.emplace(make_pair(loglevel::INFO, "polybar|infoe "));
m_prefixes.emplace(make_pair(loglevel::WARNING, "polybar|warne "));
m_prefixes.emplace(make_pair(loglevel::ERROR, "polybar|error "));
m_suffixes.emplace(make_pair(loglevel::TRACE, ""));
m_suffixes.emplace(make_pair(loglevel::INFO, ""));
m_suffixes.emplace(make_pair(loglevel::WARNING, ""));
m_suffixes.emplace(make_pair(loglevel::ERROR, ""));
}
// clang-format on
}
/**

View File

@ -2,33 +2,21 @@
#include "components/logger.hpp"
#include "components/parser.hpp"
#include "components/signals.hpp"
#include "components/types.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "utils/math.hpp"
#include "utils/string.hpp"
POLYBAR_NS
using namespace signals::parser;
/**
* Construct parser instance
*/
parser::parser(const logger& logger, const bar_settings& bar) : m_log(logger), m_bar(bar) {
assert(g_signals::parser::background_change != nullptr);
assert(g_signals::parser::foreground_change != nullptr);
assert(g_signals::parser::underline_change != nullptr);
assert(g_signals::parser::overline_change != nullptr);
assert(g_signals::parser::alignment_change != nullptr);
assert(g_signals::parser::attribute_set != nullptr);
assert(g_signals::parser::attribute_unset != nullptr);
assert(g_signals::parser::attribute_toggle != nullptr);
assert(g_signals::parser::font_change != nullptr);
assert(g_signals::parser::pixel_offset != nullptr);
assert(g_signals::parser::action_block_open != nullptr);
assert(g_signals::parser::action_block_close != nullptr);
assert(g_signals::parser::ascii_text_write != nullptr);
assert(g_signals::parser::unicode_text_write != nullptr);
assert(g_signals::parser::string_write != nullptr);
}
parser::parser(signal_emitter& emitter, const logger& logger, const bar_settings& bar)
: m_sig(emitter), m_log(logger), m_bar(bar) {}
/**
* Process input string
@ -82,61 +70,61 @@ void parser::codeblock(string data) {
switch (tag) {
case 'B':
g_signals::parser::background_change(parse_color(value, m_bar.background));
m_sig.emit(change_background{parse_color(value, m_bar.background)});
break;
case 'F':
g_signals::parser::foreground_change(parse_color(value, m_bar.foreground));
m_sig.emit(change_foreground{parse_color(value, m_bar.foreground)});
break;
case 'T':
g_signals::parser::font_change(parse_fontindex(value));
m_sig.emit(change_font{parse_fontindex(value)});
break;
case 'U':
g_signals::parser::underline_change(parse_color(value, m_bar.underline.color));
g_signals::parser::overline_change(parse_color(value, m_bar.overline.color));
m_sig.emit(change_underline{parse_color(value, m_bar.underline.color)});
m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)});
break;
case 'u':
g_signals::parser::underline_change(parse_color(value, m_bar.underline.color));
m_sig.emit(change_underline{parse_color(value, m_bar.underline.color)});
break;
case 'o':
g_signals::parser::overline_change(parse_color(value, m_bar.overline.color));
m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)});
break;
case 'R':
g_signals::parser::background_change(m_bar.foreground);
g_signals::parser::foreground_change(m_bar.background);
m_sig.emit(change_background{parse_color(value, m_bar.foreground)});
m_sig.emit(change_foreground{parse_color(value, m_bar.background)});
break;
case 'O':
g_signals::parser::pixel_offset(atoi(value.c_str()));
m_sig.emit(offset_pixel{static_cast<int16_t>(std::atoi(value.c_str()))});
break;
case 'l':
g_signals::parser::alignment_change(alignment::LEFT);
m_sig.emit(change_alignment{alignment::LEFT});
break;
case 'c':
g_signals::parser::alignment_change(alignment::CENTER);
m_sig.emit(change_alignment{alignment::CENTER});
break;
case 'r':
g_signals::parser::alignment_change(alignment::RIGHT);
m_sig.emit(change_alignment{alignment::RIGHT});
break;
case '+':
g_signals::parser::attribute_set(parse_attr(value[0]));
m_sig.emit(attribute_set{parse_attr(value[0])});
break;
case '-':
g_signals::parser::attribute_unset(parse_attr(value[0]));
m_sig.emit(attribute_unset{parse_attr(value[0])});
break;
case '!':
g_signals::parser::attribute_toggle(parse_attr(value[0]));
m_sig.emit(attribute_toggle{parse_attr(value[0])});
break;
case 'A':
@ -145,7 +133,7 @@ void parser::codeblock(string data) {
mousebtn btn = parse_action_btn(data);
m_actions.push_back(static_cast<int>(btn));
g_signals::parser::action_block_open(btn, value);
m_sig.emit(action_begin{action{btn, value}});
// make sure we strip the correct length (btn+wrapping colons)
if (value[0] != ':') {
@ -153,7 +141,7 @@ void parser::codeblock(string data) {
}
value += "::";
} else if (!m_actions.empty()) {
g_signals::parser::action_block_close(parse_action_btn(value));
m_sig.emit(action_end{parse_action_btn(value)});
m_actions.pop_back();
}
break;
@ -172,7 +160,7 @@ void parser::codeblock(string data) {
* Process text contents
*/
size_t parser::text(string data) {
uint8_t* utf = (uint8_t*)data.c_str();
uint8_t* utf = reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str()));
if (utf[0] < 0x80) {
// grab all consecutive ascii chars
@ -184,25 +172,26 @@ size_t parser::text(string data) {
while (utf[n] != '\0' && utf[++n] < 0x80) {
;
}
g_signals::parser::string_write(data.substr(0, n).c_str(), n);
m_sig.emit(write_text_string{data.substr(0, n)});
return n;
} else if ((utf[0] & 0xe0) == 0xc0) { // 2 byte utf-8 sequence
g_signals::parser::unicode_text_write((utf[0] & 0x1f) << 6 | (utf[1] & 0x3f));
m_sig.emit(write_text_unicode{static_cast<uint16_t>((utf[0] & 0x1f) << 6 | (utf[1] & 0x3f))});
return 2;
} else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence
g_signals::parser::unicode_text_write((utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f));
m_sig.emit(
write_text_unicode{static_cast<uint16_t>((utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f))});
return 3;
} else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence
g_signals::parser::unicode_text_write(0xfffd);
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 4;
} else if ((utf[0] & 0xfc) == 0xf8) { // 5 byte utf-8 sequence
g_signals::parser::unicode_text_write(0xfffd);
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 5;
} else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence
g_signals::parser::unicode_text_write(0xfffd);
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
return 6;
} else { // invalid utf-8 sequence
g_signals::parser::ascii_text_write(utf[0]);
m_sig.emit(write_text_ascii{utf[0]});
return 1;
}
}

View File

@ -2,6 +2,9 @@
#include "components/logger.hpp"
#include "components/types.hpp"
#include "errors.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal.hpp"
#include "x11/connection.hpp"
#include "x11/draw.hpp"
#include "x11/fonts.hpp"
@ -12,30 +15,19 @@
POLYBAR_NS
/**
* Configure injection module
*/
di::injector<unique_ptr<renderer>> configure_renderer(const bar_settings& bar, const vector<string>& fonts) {
// clang-format off
return di::make_injector(
di::bind<>().to(bar),
di::bind<>().to(fonts),
configure_connection(),
configure_logger(),
configure_font_manager());
// clang-format on
}
/**
* Construct renderer instance
*/
renderer::renderer(connection& conn, const logger& logger, unique_ptr<font_manager> font_manager,
const bar_settings& bar, const vector<string>& fonts)
renderer::renderer(connection& conn, signal_emitter& emitter, const logger& logger,
unique_ptr<font_manager> font_manager, const bar_settings& bar, const vector<string>& fonts)
: m_connection(conn)
, m_sig(emitter)
, m_log(logger)
, m_fontmanager(forward<decltype(font_manager)>(font_manager))
, m_bar(bar)
, m_rect(bar.inner_area()) {
, m_bar(forward<const bar_settings&>(bar))
, m_rect(m_bar.inner_area()) {
m_sig.attach(this);
m_log.trace("renderer: Get TrueColor visual");
m_visual = m_connection.visual_type(m_connection.screen(), 32).get();
@ -127,11 +119,9 @@ renderer::renderer(connection& conn, const logger& logger, unique_ptr<font_manag
if (!fonts_loaded && !fonts.empty()) {
m_log.warn("Unable to load fonts, using fallback font \"fixed\"");
}
if (!fonts_loaded && !m_fontmanager->load("fixed")) {
throw application_error("Unable to load fonts");
}
m_fontmanager->allocate_color(m_bar.foreground, true);
}
}
@ -140,6 +130,8 @@ renderer::renderer(connection& conn, const logger& logger, unique_ptr<font_manag
* Deconstruct instance
*/
renderer::~renderer() {
m_sig.detach(this);
if (m_window != XCB_NONE) {
m_connection.destroy_window(m_window);
}
@ -183,7 +175,7 @@ void renderer::end() {
}
/**
* Redraw window contents
* Flush pixmap contents onto the target window
*/
void renderer::flush(bool clear) {
const xcb_rectangle_t& r = m_rect;
@ -291,110 +283,14 @@ void renderer::reserve_space(edge side, uint16_t w) {
}
/**
* Change value of background gc
*/
void renderer::set_background(const uint32_t color) {
if (m_colors[gc::BG] == color) {
return m_log.trace_x("renderer: ignoring unchanged background color(#%08x)", color);
}
m_log.trace_x("renderer: set_background(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::BG), XCB_GC_FOREGROUND, &color);
m_colors[gc::BG] = color;
shift_content(0);
}
/**
* Change value of foreground gc
*/
void renderer::set_foreground(const uint32_t color) {
if (m_colors[gc::FG] == color) {
return m_log.trace_x("renderer: ignoring unchanged foreground color(#%08x)", color);
}
m_log.trace_x("renderer: set_foreground(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::FG), XCB_GC_FOREGROUND, &color);
m_fontmanager->allocate_color(color);
m_colors[gc::FG] = color;
}
/**
* Change value of underline gc
*/
void renderer::set_underline(const uint32_t color) {
if (m_colors[gc::UL] == color) {
return m_log.trace_x("renderer: ignoring unchanged underline color(#%08x)", color);
}
m_log.trace_x("renderer: set_underline(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::UL), XCB_GC_FOREGROUND, &color);
m_colors[gc::UL] = color;
}
/**
* Change value of overline gc
*/
void renderer::set_overline(const uint32_t color) {
if (m_colors[gc::OL] == color) {
return m_log.trace_x("renderer: ignoring unchanged overline color(#%08x)", color);
}
m_log.trace_x("renderer: set_overline(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::OL), XCB_GC_FOREGROUND, &color);
m_colors[gc::OL] = color;
}
/**
* Change preferred font index used when matching glyphs
*/
void renderer::set_fontindex(const int8_t font) {
if (m_fontindex == font) {
return m_log.trace_x("renderer: ignoring unchanged font index(%i)", static_cast<int8_t>(font));
}
m_log.trace_x("renderer: set_fontindex(%i)", static_cast<int8_t>(font));
m_fontmanager->set_preferred_font(font);
m_fontindex = font;
}
/**
* Change current alignment
*/
void renderer::set_alignment(const alignment align) {
if (align == m_alignment) {
return m_log.trace_x("renderer: ignoring unchanged alignment(%i)", static_cast<uint8_t>(align));
}
m_log.trace_x("renderer: set_alignment(%i)", static_cast<uint8_t>(align));
m_alignment = align;
m_currentx = 0;
}
/**
* Enable/remove attribute
*/
void renderer::set_attribute(const attribute attr, bool state) {
m_log.trace_x("renderer: set_attribute(%i, %i)", static_cast<uint8_t>(attr), state);
if (state) {
m_attributes |= 1U << static_cast<uint8_t>(attr);
} else {
m_attributes &= ~(1U << static_cast<uint8_t>(attr));
}
}
/**
* Toggle attribute
*/
void renderer::toggle_attribute(const attribute attr) {
m_log.trace_x("renderer: toggle_attribute(%i)", static_cast<uint8_t>(attr));
m_attributes ^= 1U << static_cast<uint8_t>(attr);
}
/**
* Check if the given attribute is set
* Check if given attribute is enabled
*/
bool renderer::check_attribute(const attribute attr) {
return (m_attributes >> static_cast<uint8_t>(attr)) & 1U;
}
/**
* Fill background area
* Fill background color
*/
void renderer::fill_background() {
m_log.trace_x("renderer: fill_background");
@ -402,7 +298,7 @@ void renderer::fill_background() {
}
/**
* Fill overline area
* Fill overline color
*/
void renderer::fill_overline(int16_t x, uint16_t w) {
if (!check_attribute(attribute::OVERLINE)) {
@ -415,7 +311,7 @@ void renderer::fill_overline(int16_t x, uint16_t w) {
}
/**
* Fill underline area
* Fill underline color
*/
void renderer::fill_underline(int16_t x, uint16_t w) {
if (!check_attribute(attribute::UNDERLINE)) {
@ -429,7 +325,7 @@ void renderer::fill_underline(int16_t x, uint16_t w) {
}
/**
* Shift filled area by given pixels
* @see shift_content
*/
void renderer::fill_shift(const int16_t px) {
shift_content(px);
@ -469,7 +365,7 @@ void renderer::draw_character(uint16_t character) {
}
/**
* Draw character glyphs
* Draw consecutive character glyphs
*/
void renderer::draw_textstring(const char* text, size_t len) {
m_log.trace_x("renderer: draw_textstring(\"%s\")", text);
@ -493,7 +389,6 @@ void renderer::draw_textstring(const char* text, size_t len) {
chars.emplace_back(text[++n]);
}
// TODO: cache
auto width = m_fontmanager->char_width(font, chars[0]) * chars.size();
auto x = shift_content(width);
auto y = m_rect.height / 2 + font->height / 2 - font->descent + font->offset_y;
@ -517,70 +412,14 @@ void renderer::draw_textstring(const char* text, size_t len) {
}
/**
* Create new action block at the current position
*/
void renderer::begin_action(const mousebtn btn, const string& cmd) {
action_block action{};
action.button = btn;
action.align = m_alignment;
action.start_x = m_currentx;
action.command = string_util::replace_all(cmd, ":", "\\:");
action.active = true;
if (action.button == mousebtn::NONE) {
action.button = mousebtn::LEFT;
}
m_log.trace_x("renderer: begin_action(%i, %s)", static_cast<uint8_t>(action.button), cmd.c_str());
m_actions.emplace_back(action);
}
/**
* End action block at the current position
*/
void renderer::end_action(const mousebtn btn) {
int16_t clickable_width{0};
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
if (!action->active || action->align != m_alignment || action->button != btn) {
continue;
}
action->active = false;
switch (action->align) {
case alignment::NONE:
break;
case alignment::LEFT:
action->end_x = m_currentx;
break;
case alignment::CENTER:
clickable_width = m_currentx - action->start_x;
action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2;
action->end_x = action->start_x + clickable_width;
break;
case alignment::RIGHT:
action->start_x = m_rect.width - m_currentx + action->start_x;
action->end_x = m_rect.width;
break;
}
action->start_x += m_bar.pos.x + m_rect.x;
action->end_x += m_bar.pos.x + m_rect.x;
m_log.trace_x("renderer: end_action(%i, %s, %i)", static_cast<uint8_t>(btn), action->command, action->width());
return;
}
}
/**
* Get all action blocks
* Get completed action blocks
*/
const vector<action_block> renderer::get_actions() {
return m_actions;
}
/**
* Shift contents by given pixel value
* Shift pixmap content by given value
*/
int16_t renderer::shift_content(int16_t x, const int16_t shift_x) {
m_log.trace_x("renderer: shift_content(%i)", shift_x);
@ -628,7 +467,7 @@ int16_t renderer::shift_content(int16_t x, const int16_t shift_x) {
}
/**
* Shift contents by given pixel value
* @see shift_content
*/
int16_t renderer::shift_content(const int16_t shift_x) {
return shift_content(m_currentx, shift_x);
@ -636,7 +475,7 @@ int16_t renderer::shift_content(const int16_t shift_x) {
#ifdef DEBUG_HINTS
/**
* Draw debugging hints onto the output window
* Draw boxes at the location of each created action block
*/
void renderer::debug_hints() {
uint16_t border_width{1};
@ -685,4 +524,185 @@ void renderer::debug_hints() {
}
#endif
bool renderer::on(const change_background& evt) {
uint32_t color{*evt()};
if (m_colors[gc::BG] == color) {
m_log.trace_x("renderer: ignoring unchanged background color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_background(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::BG), XCB_GC_FOREGROUND, &color);
m_colors[gc::BG] = color;
shift_content(0);
}
return true;
}
bool renderer::on(const change_foreground& evt) {
uint32_t color{*evt()};
if (m_colors[gc::FG] == color) {
m_log.trace_x("renderer: ignoring unchanged foreground color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_foreground(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::FG), XCB_GC_FOREGROUND, &color);
m_fontmanager->allocate_color(color);
m_colors[gc::FG] = color;
}
return true;
}
bool renderer::on(const change_underline& evt) {
uint32_t color{*evt()};
if (m_colors[gc::UL] == color) {
m_log.trace_x("renderer: ignoring unchanged underline color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_underline(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::UL), XCB_GC_FOREGROUND, &color);
m_colors[gc::UL] = color;
}
return true;
}
bool renderer::on(const change_overline& evt) {
uint32_t color{*evt()};
if (m_colors[gc::OL] == color) {
m_log.trace_x("renderer: ignoring unchanged overline color(#%08x)", color);
} else {
m_log.trace_x("renderer: set_overline(#%08x)", color);
m_connection.change_gc(m_gcontexts.at(gc::OL), XCB_GC_FOREGROUND, &color);
m_colors[gc::OL] = color;
}
return true;
}
bool renderer::on(const change_font& evt) {
int8_t font{*evt()};
if (m_fontindex == font) {
m_log.trace_x("renderer: ignoring unchanged font index(%i)", static_cast<int8_t>(font));
} else {
m_log.trace_x("renderer: set_fontindex(%i)", static_cast<int8_t>(font));
m_fontmanager->set_preferred_font(font);
m_fontindex = font;
}
return true;
}
bool renderer::on(const change_alignment& evt) {
alignment align{*evt()};
if (align == m_alignment) {
m_log.trace_x("renderer: ignoring unchanged alignment(%i)", static_cast<uint8_t>(align));
} else {
m_log.trace_x("renderer: set_alignment(%i)", static_cast<uint8_t>(align));
m_alignment = align;
m_currentx = 0;
}
return true;
}
bool renderer::on(const offset_pixel& evt) {
shift_content(*evt());
return true;
}
bool renderer::on(const attribute_set& evt) {
m_log.trace_x("renderer: attribute_set(%i, %i)", static_cast<uint8_t>(*evt()), true);
m_attributes |= 1U << static_cast<uint8_t>(*evt());
return true;
}
bool renderer::on(const attribute_unset& evt) {
m_log.trace_x("renderer: attribute_unset(%i, %i)", static_cast<uint8_t>(*evt()), true);
m_attributes &= ~(1U << static_cast<uint8_t>(*evt()));
return true;
}
bool renderer::on(const attribute_toggle& evt) {
m_log.trace_x("renderer: attribute_toggle(%i)", static_cast<uint8_t>(*evt()));
m_attributes ^= 1U << static_cast<uint8_t>(*evt());
return true;
}
bool renderer::on(const action_begin& evt) {
action a{*evt()};
action_block action{};
action.button = a.button;
action.align = m_alignment;
action.start_x = m_currentx;
action.command = string_util::replace_all(a.command, ":", "\\:");
action.active = true;
if (action.button == mousebtn::NONE) {
action.button = mousebtn::LEFT;
}
m_log.trace_x("renderer: action_begin(%i, %s)", static_cast<uint8_t>(a.button), a.command.c_str());
m_actions.emplace_back(action);
return true;
}
bool renderer::on(const action_end& evt) {
mousebtn btn{*evt()};
int16_t clickable_width{0};
for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
if (!action->active || action->align != m_alignment || action->button != btn) {
continue;
}
action->active = false;
switch (action->align) {
case alignment::NONE:
break;
case alignment::LEFT:
action->end_x = m_currentx;
break;
case alignment::CENTER:
clickable_width = m_currentx - action->start_x;
action->start_x = m_rect.width / 2 - clickable_width / 2 + action->start_x / 2;
action->end_x = action->start_x + clickable_width;
break;
case alignment::RIGHT:
action->start_x = m_rect.width - m_currentx + action->start_x;
action->end_x = m_rect.width;
break;
}
action->start_x += m_bar.pos.x + m_rect.x;
action->end_x += m_bar.pos.x + m_rect.x;
m_log.trace_x("renderer: action_end(%i, %s, %i)", static_cast<uint8_t>(btn), action->command, action->width());
}
return true;
}
bool renderer::on(const write_text_ascii& evt) {
draw_character(*evt());
return true;
}
bool renderer::on(const write_text_unicode& evt) {
draw_character(*evt());
return true;
}
bool renderer::on(const write_text_string& evt) {
string text{*evt()};
draw_textstring(text.c_str(), text.size());
return true;
}
POLYBAR_NS_END

View File

@ -5,33 +5,28 @@
#include "components/eventloop.hpp"
#include "components/logger.hpp"
#include "components/screen.hpp"
#include "components/signals.hpp"
#include "components/types.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "x11/connection.hpp"
#include "x11/randr.hpp"
#include "x11/winspec.hpp"
POLYBAR_NS
/**
* Configure injection module
*/
di::injector<unique_ptr<screen>> configure_screen() {
return di::make_injector(configure_connection(), configure_logger(), configure_config());
}
using namespace signals::eventloop;
/**
* Construct screen instance
*/
screen::screen(connection& conn, const logger& logger, const config& conf)
screen::screen(connection& conn, signal_emitter& emitter, const logger& logger, const config& conf)
: m_connection(conn)
, m_sig(emitter)
, m_log(logger)
, m_conf(conf)
, m_root(conn.root())
, m_monitors(randr_util::get_monitors(m_connection, m_root, true))
, m_size({conn.screen()->width_in_pixels, conn.screen()->height_in_pixels}) {
assert(g_signals::event::enqueue != nullptr);
// Check if the reloading has been disabled by the user
if (!m_conf.get<bool>("settings", "screenchange-reload", false)) {
return;
@ -105,8 +100,8 @@ void screen::handle(const evt::randr_screen_change_notify& evt) {
}
m_log.warn("randr_screen_change_notify (%ux%u)... reloading", evt->width, evt->height);
m_sig.emit(process_quit{eventloop::make_quit_evt(true)});
m_sigraised = true;
g_signals::event::enqueue(eventloop::make(quit_event{}, true));
}
POLYBAR_NS_END

View File

@ -1,45 +0,0 @@
#include "components/signals.hpp"
#include "components/types.hpp"
POLYBAR_NS
namespace g_signals {
/**
* Signals used to communicate with the event loop
*/
namespace event {
callback<const eventloop::entry_t&> enqueue{noop<const eventloop::entry_t&>};
callback<const eventloop::entry_t&> enqueue_delayed{noop<const eventloop::entry_t&>};
}
/**
* Signals used to communicate with the bar window
*/
namespace bar {
callback<const string> action_click{noop<const string>};
callback<const bool> visibility_change{noop<const bool>};
}
/**
* Signals used to communicate with the input parser
*/
namespace parser {
callback<const uint32_t> background_change{noop<const uint32_t>};
callback<const uint32_t> foreground_change{noop<const uint32_t>};
callback<const uint32_t> underline_change{noop<const uint32_t>};
callback<const uint32_t> overline_change{noop<const uint32_t>};
callback<const alignment> alignment_change{noop<const alignment>};
callback<const attribute> attribute_set{noop<const attribute>};
callback<const attribute> attribute_unset{noop<const attribute>};
callback<const attribute> attribute_toggle{noop<const attribute>};
callback<const int8_t> font_change{noop<const int8_t>};
callback<const int16_t> pixel_offset{noop<const int16_t>};
callback<const mousebtn, const string> action_block_open{noop<const mousebtn, const string>};
callback<const mousebtn> action_block_close{noop<const mousebtn>};
callback<const uint16_t> ascii_text_write{noop<const uint16_t>};
callback<const uint16_t> unicode_text_write{noop<const uint16_t>};
callback<const char*, const size_t> string_write{noop<const char*, const size_t>};
}
}
POLYBAR_NS_END

View File

@ -61,7 +61,7 @@ namespace drawtypes {
auto framerate = conf.get<int>(section, name + "-framerate", 1000);
return make_shared<animation>(move(vec), framerate);
return factory_util::shared<animation>(move(vec), framerate);
}
}

View File

@ -19,7 +19,7 @@ namespace drawtypes {
std::back_insert_iterator<decltype(tokens)> back_it(tokens);
std::copy(m_tokens.begin(), m_tokens.end(), back_it);
}
return make_shared<label>(m_text, m_foreground, m_background, m_underline, m_overline, m_font, m_padding, m_margin,
return factory_util::shared<label>(m_text, m_foreground, m_background, m_underline, m_overline, m_font, m_padding, m_margin,
m_maxlen, m_ellipsis, move(tokens));
}

View File

@ -45,7 +45,7 @@ namespace drawtypes {
vec.emplace_back(move(icon));
}
return make_shared<ramp>(move(vec));
return factory_util::shared<ramp>(move(vec));
}
}

View File

@ -0,0 +1,8 @@
#include "events/signal_emitter.hpp"
#include "events/signal_receiver.hpp"
POLYBAR_NS
signal_receivers_t g_signal_receivers;
POLYBAR_NS_END

View File

@ -0,0 +1 @@
#include "events/signal_receiver.hpp"

View File

@ -2,7 +2,6 @@
#include "common.hpp"
#include "components/bar.hpp"
#include "components/command_line.hpp"
#include "components/config.hpp"
#include "components/controller.hpp"
@ -11,7 +10,6 @@
#include "utils/env.hpp"
#include "utils/inotify.hpp"
#include "utils/process.hpp"
#include "x11/ewmh.hpp"
#include "x11/xutils.hpp"
using namespace polybar;
@ -34,55 +32,69 @@ int main(int argc, char** argv) {
};
// clang-format on
logger& logger{configure_logger<decltype(logger)>(loglevel::WARNING).create<decltype(logger)>()};
uint8_t exit_code{EXIT_SUCCESS};
bool reload{false};
logger& logger{const_cast<class logger&>(make_logger(loglevel::WARNING))};
try {
//==================================================
// Connect to X server
//==================================================
XInitThreads();
xcb_connection_t* xcbconn{nullptr};
if (!xutils::get_connection()) {
if ((xcbconn = xutils::get_connection()) == nullptr) {
logger.err("A connection to X could not be established... ");
throw exit_failure{};
}
connection{xcbconn}.preload_atoms();
connection{xcbconn}.query_extensions();
//==================================================
// Block all signals by default
//==================================================
sigset_t blockmask;
sigfillset(&blockmask);
if (pthread_sigmask(SIG_BLOCK, &blockmask, nullptr) == -1) {
throw system_error("Failed to block signals");
}
//==================================================
// Parse command line arguments
//==================================================
string scriptname{argv[0]};
vector<string> args(argv + 1, argv + argc);
cliparser cli{configure_cliparser<decltype(cli)>(scriptname, opts).create<decltype(cli)>()};
cli.process_input(args);
unique_ptr<cliparser> cli{make_command_line(scriptname, opts)};
cli->process_input(args);
if (cli.has("quiet")) {
if (cli->has("quiet")) {
logger.verbosity(loglevel::ERROR);
} else if (cli.has("log")) {
logger.verbosity(cli.get("log"));
} else if (cli->has("log")) {
logger.verbosity(cli->get("log"));
}
if (cli.has("help")) {
cli.usage();
if (cli->has("help")) {
cli->usage();
throw exit_success{};
} else if (cli.has("version")) {
} else if (cli->has("version")) {
print_build_info(version_details(args));
throw exit_success{};
} else if (args.empty() || args[0][0] == '-') {
cli.usage();
cli->usage();
throw exit_failure{};
}
//==================================================
// Load user configuration
//==================================================
config& conf{configure_config<decltype(conf)>().create<decltype(conf)>()};
config& conf{const_cast<config&>(make_confreader())};
if (cli.has("config")) {
conf.load(cli.get("config"), args[0]);
if (cli->has("config")) {
conf.load(cli->get("config"), args[0]);
} else if (env_util::has("XDG_CONFIG_HOME")) {
conf.load(env_util::get("XDG_CONFIG_HOME") + "/polybar/config", args[0]);
} else if (env_util::has("HOME")) {
@ -94,37 +106,43 @@ int main(int argc, char** argv) {
//==================================================
// Dump requested data
//==================================================
if (cli.has("dump")) {
std::cout << conf.get<string>(conf.bar_section(), cli.get("dump")) << std::endl;
if (cli->has("dump")) {
std::cout << conf.get<string>(conf.bar_section(), cli->get("dump")) << std::endl;
throw exit_success{};
}
//==================================================
// Create config watch if we should track changes
// Create controller and run application
//==================================================
inotify_util::watch_t watch;
unique_ptr<controller> ctrl;
bool enable_ipc{conf.get<bool>(conf.bar_section(), "enable-ipc", false)};
watch_t confwatch;
if (cli.has("reload")) {
watch = inotify_util::make_watch(conf.filepath());
if (cli->has("print-wmname")) {
enable_ipc = false;
} else if (cli->has("reload")) {
inotify_util::make_watch(conf.filepath());
}
//==================================================
// Create controller
//==================================================
auto ctrl = configure_controller(watch).create<unique_ptr<controller>>();
ctrl = make_controller(move(confwatch), enable_ipc, cli->has("stdout"));
ctrl->bootstrap(cli.has("stdout"), cli.has("print-wmname"));
if (cli.has("print-wmname")) {
if (cli->has("print-wmname")) {
std::cout << ctrl->opts().wmname << std::endl;
throw exit_success{};
}
//==================================================
// Run application
//==================================================
ctrl->setup();
if (!ctrl->run()) {
reload = true;
}
//==================================================
// Unblock signals
//==================================================
if (pthread_sigmask(SIG_UNBLOCK, &blockmask, nullptr) == -1) {
throw system_error("Failed to unblock signals");
}
} catch (const exit_success& term) {
exit_code = EXIT_SUCCESS;
} catch (const exit_failure& term) {

View File

@ -118,13 +118,13 @@ namespace modules {
m_modelabels.emplace(mode::NODE_PRIVATE, load_optional_label(m_conf, name(), "label-private"));
}
m_icons = make_shared<iconset>();
m_icons->add(DEFAULT_ICON, make_shared<label>(m_conf.get<string>(name(), DEFAULT_ICON, "")));
m_icons = factory_util::shared<iconset>();
m_icons->add(DEFAULT_ICON, factory_util::shared<label>(m_conf.get<string>(name(), DEFAULT_ICON, "")));
for (const auto& workspace : m_conf.get_list<string>(name(), "ws-icon", {})) {
auto vec = string_util::split(workspace, ';');
if (vec.size() == 2) {
m_icons->add(vec[0], make_shared<label>(vec[1]));
m_icons->add(vec[0], factory_util::shared<label>(vec[1]));
}
}
}

View File

@ -55,13 +55,13 @@ namespace modules {
m_modelabel = load_optional_label(m_conf, name(), "label-mode", "%mode%");
}
m_icons = make_shared<iconset>();
m_icons->add(DEFAULT_WS_ICON, make_shared<label>(m_conf.get<string>(name(), DEFAULT_WS_ICON, "")));
m_icons = factory_util::shared<iconset>();
m_icons->add(DEFAULT_WS_ICON, factory_util::shared<label>(m_conf.get<string>(name(), DEFAULT_WS_ICON, "")));
for (const auto& workspace : m_conf.get_list<string>(name(), "ws-icon", {})) {
auto vec = string_util::split(workspace, ';');
if (vec.size() == 2) {
m_icons->add(vec[0], make_shared<label>(vec[1]));
m_icons->add(vec[0], factory_util::shared<label>(vec[1]));
}
}

View File

@ -30,7 +30,7 @@ namespace modules {
m_formatter->add(FORMAT_OFFLINE, "", {TAG_LABEL_OFFLINE});
m_icons = make_shared<iconset>();
m_icons = factory_util::shared<iconset>();
if (m_formatter->has(TAG_ICON_PLAY) || m_formatter->has(TAG_TOGGLE) || m_formatter->has(TAG_TOGGLE_STOP)) {
m_icons->add("play", load_icon(m_conf, name(), TAG_ICON_PLAY));

View File

@ -21,7 +21,7 @@ namespace modules {
*/
xbacklight_module::xbacklight_module(const bar_settings& bar, const logger& logger, const config& config, string name)
: static_module<xbacklight_module>(bar, logger, config, name)
, m_connection(configure_connection().create<connection&>()) {}
, m_connection(make_connection()) {}
/**
* Bootstrap the module by grabbing all required components
@ -60,9 +60,6 @@ namespace modules {
throw module_error("Failed to create event proxy");
}
// Get the throttle time
m_randrnotify.offset = xutils::event_timer_ms(m_conf, xcb_randr_notify_event_t{});
// Connect with the event registry and make sure we get
// notified when a RandR output property gets modified
m_connection.attach_sink(this, SINK_PRIORITY_MODULE);
@ -106,8 +103,6 @@ namespace modules {
return;
} else if (evt->u.op.atom != m_output->backlight.atom) {
return;
} else if (m_randrnotify.deny(evt->u.op.timestamp)) {
return m_log.trace_x("%s: Ignoring randr notify (throttled)...", name());
} else {
update();
}
@ -142,8 +137,8 @@ namespace modules {
// with the cmd handlers
string output{module::get_output()};
m_builder->cmd(mousebtn::SCROLL_UP, EVENT_SCROLLUP, m_scroll && m_percentage < 100);
m_builder->cmd(mousebtn::SCROLL_DOWN, EVENT_SCROLLDOWN, m_scroll && m_percentage > 0);
m_builder->cmd(mousebtn::SCROLL_UP, string{EVENT_SCROLLUP}, m_scroll && m_percentage < 100);
m_builder->cmd(mousebtn::SCROLL_DOWN, string{EVENT_SCROLLDOWN}, m_scroll && m_percentage > 0);
m_builder->append(output);

View File

@ -18,7 +18,7 @@ namespace modules {
*/
xkeyboard_module::xkeyboard_module(const bar_settings& bar, const logger& logger, const config& config, string name)
: static_module<xkeyboard_module>(bar, logger, config, name)
, m_connection(configure_connection().create<connection&>()) {}
, m_connection(make_connection()) {}
/**
* Bootstrap the module

View File

@ -18,7 +18,7 @@ namespace modules {
* currently active to enable title tracking
*/
active_window::active_window(xcb_window_t win)
: m_connection(configure_connection().create<decltype(m_connection)>()), m_window(m_connection, win) {
: m_connection(make_connection()), m_window(m_connection, win) {
try {
m_window.change_event_mask(XCB_EVENT_MASK_PROPERTY_CHANGE);
} catch (const xpp::x::error::window& err) {
@ -66,7 +66,7 @@ namespace modules {
*/
xwindow_module::xwindow_module(const bar_settings& bar, const logger& logger, const config& config, string name)
: static_module<xwindow_module>(bar, logger, config, name)
, m_connection(configure_connection().create<connection&>()) {}
, m_connection(make_connection()) {}
/**
* Bootstrap the module

View File

@ -22,7 +22,7 @@ namespace modules {
xworkspaces_module::xworkspaces_module(
const bar_settings& bar, const logger& logger, const config& config, string name)
: static_module<xworkspaces_module>(bar, logger, config, name)
, m_connection(configure_connection().create<connection&>()) {}
, m_connection(make_connection()) {}
/**
* Bootstrap the module
@ -73,13 +73,13 @@ namespace modules {
// clang-format on
}
m_icons = make_shared<iconset>();
m_icons->add(DEFAULT_ICON, make_shared<label>(m_conf.get<string>(name(), DEFAULT_ICON, "")));
m_icons = factory_util::shared<iconset>();
m_icons->add(DEFAULT_ICON, factory_util::shared<label>(m_conf.get<string>(name(), DEFAULT_ICON, "")));
for (const auto& workspace : m_conf.get_list<string>(name(), "icon", {})) {
auto vec = string_util::split(workspace, ';');
if (vec.size() == 2) {
m_icons->add(vec[0], make_shared<label>(vec[1]));
m_icons->add(vec[0], factory_util::shared<label>(vec[1]));
}
}

View File

@ -105,14 +105,13 @@ namespace inotify_util {
return m_path;
}
watch_t make_watch(string path) {
di::injector<watch_t> injector = di::make_injector(di::bind<>().to(path));
return injector.create<watch_t>();
}
bool match(const event_t* evt, int mask) {
return (evt->mask & mask) == mask;
}
watch_t make_watch(string path) {
return watch_t{new watch_t::element_type{path}};
}
}
POLYBAR_NS_END

View File

@ -1,5 +1,6 @@
#include <iomanip>
#include <utility>
#include <map>
#include "errors.hpp"
#include "utils/color.hpp"
@ -8,7 +9,7 @@
POLYBAR_NS
map<string, color> g_colorstore;
std::map<string, color> g_colorstore;
color g_colorempty{"#00000000"};
color g_colorblack{"#ff000000"};
color g_colorwhite{"#ffffffff"};

View File

@ -8,14 +8,6 @@
POLYBAR_NS
/**
* Configure injection module
*/
di::injector<connection&> configure_connection() {
return di::make_injector(di::bind<>().to(
factory_util::generic_singleton<connection>(xutils::get_connection(), xutils::get_connection_fd())));
}
/**
* Preload required xcb atoms
*/

View File

@ -1,6 +1,7 @@
#include <X11/Xlib-xcb.h>
#include "utils/color.hpp"
#include "utils/factory.hpp"
#include "utils/memory.hpp"
#include "x11/connection.hpp"
#include "x11/fonts.hpp"
@ -12,8 +13,8 @@ POLYBAR_NS
/**
* Configure injection module
*/
di::injector<unique_ptr<font_manager>> configure_font_manager() {
return di::make_injector(configure_connection(), configure_logger());
unique_ptr<font_manager> make_font_manager() {
return factory_util::unique<font_manager>(make_connection(), make_logger());
}
array<char, XFT_MAXCHARS> xft_widths;

122
src/x11/tray_client.cpp Normal file
View File

@ -0,0 +1,122 @@
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include "utils/memory.hpp"
#include "x11/connection.hpp"
#include "x11/tray_client.hpp"
#include "x11/xembed.hpp"
#include "x11/xutils.hpp"
POLYBAR_NS
tray_client::tray_client(connection& conn, xcb_window_t win, uint16_t w, uint16_t h)
: m_connection(conn), m_window(win), m_width(w), m_height(h) {
m_xembed = memory_util::make_malloc_ptr<xembed_data>();
m_xembed->version = XEMBED_VERSION;
m_xembed->flags = XEMBED_MAPPED;
}
tray_client::~tray_client() {
xembed::unembed(m_connection, window(), m_connection.root());
}
uint16_t tray_client::width() const {
return m_width;
}
uint16_t tray_client::height() const {
return m_height;
}
void tray_client::clear_window() const {
try {
m_connection.clear_area_checked(1, window(), 0, 0, width(), height());
} catch (const xpp::x::error::window& err) {
// ignore
}
}
/**
* Match given window against client window
*/
bool tray_client::match(const xcb_window_t& win) const {
return win == m_window;
}
/**
* Get client window mapped state
*/
bool tray_client::mapped() const {
return m_mapped;
}
/**
* Set client window mapped state
*/
void tray_client::mapped(bool state) {
m_mapped = state;
}
/**
* Get client window
*/
xcb_window_t tray_client::window() const {
return m_window;
}
/**
* Get xembed data pointer
*/
xembed_data* tray_client::xembed() const {
return m_xembed.get();
}
/**
* Make sure that the window mapping state is correct
*/
void tray_client::ensure_state() const {
if (!mapped() && ((xembed()->flags & XEMBED_MAPPED) == XEMBED_MAPPED)) {
m_connection.map_window_checked(window());
} else if (mapped() && ((xembed()->flags & XEMBED_MAPPED) != XEMBED_MAPPED)) {
m_connection.unmap_window_checked(window());
}
}
/**
* Configure window size
*/
void tray_client::reconfigure(int16_t x, int16_t y) const {
uint32_t configure_mask = 0;
uint32_t configure_values[7];
xcb_params_configure_window_t configure_params;
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, width, m_width);
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, height, m_height);
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, x, x);
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, y, y);
xutils::pack_values(configure_mask, &configure_params, configure_values);
m_connection.configure_window_checked(window(), configure_mask, configure_values);
}
/**
* Respond to client resize requests
*/
void tray_client::configure_notify(int16_t x, int16_t y) const {
auto notify = memory_util::make_malloc_ptr<xcb_configure_notify_event_t>(32);
notify->response_type = XCB_CONFIGURE_NOTIFY;
notify->event = m_window;
notify->window = m_window;
notify->override_redirect = false;
notify->above_sibling = 0;
notify->x = x;
notify->y = y;
notify->width = m_width;
notify->height = m_height;
notify->border_width = 0;
const char* data = reinterpret_cast<const char*>(notify.get());
m_connection.send_event_checked(false, m_window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, data);
}
POLYBAR_NS_END

View File

@ -2,20 +2,22 @@
#include <xcb/xcb_image.h>
#include <thread>
#include "components/signals.hpp"
#include "components/config.hpp"
#include "components/eventloop.hpp"
#include "components/types.hpp"
#include "errors.hpp"
#include "events/signal.hpp"
#include "utils/color.hpp"
#include "utils/factory.hpp"
#include "utils/math.hpp"
#include "utils/memory.hpp"
#include "utils/process.hpp"
#include "x11/atoms.hpp"
#include "x11/color.hpp"
#include "x11/connection.hpp"
#include "x11/draw.hpp"
#include "x11/ewmh.hpp"
#include "x11/graphics.hpp"
#include "x11/tray.hpp"
#include "x11/tray_manager.hpp"
#include "x11/window.hpp"
#include "x11/winspec.hpp"
#include "x11/wm.hpp"
@ -39,135 +41,136 @@
POLYBAR_NS
// implementation : tray_client {{{
using namespace wm_util;
tray_client::tray_client(connection& conn, xcb_window_t win, uint16_t w, uint16_t h)
: m_connection(conn), m_window(win), m_width(w), m_height(h) {
m_xembed = memory_util::make_malloc_ptr<xembed_data>();
m_xembed->version = XEMBED_VERSION;
m_xembed->flags = XEMBED_MAPPED;
}
tray_client::~tray_client() {
xembed::unembed(m_connection, window(), m_connection.root());
}
uint16_t tray_client::width() const {
return m_width;
}
uint16_t tray_client::height() const {
return m_height;
}
void tray_client::clear_window() const {
try {
m_connection.clear_area_checked(1, window(), 0, 0, width(), height());
} catch (const xpp::x::error::window& err) {
// ignore
}
}
/**
* Match given window against client window
*/
bool tray_client::match(const xcb_window_t& win) const {
return win == m_window;
}
/**
* Get client window mapped state
*/
bool tray_client::mapped() const {
return m_mapped;
}
/**
* Set client window mapped state
*/
void tray_client::mapped(bool state) {
m_mapped = state;
}
/**
* Get client window
*/
xcb_window_t tray_client::window() const {
return m_window;
}
/**
* Get xembed data pointer
*/
xembed_data* tray_client::xembed() const {
return m_xembed.get();
}
/**
* Make sure that the window mapping state is correct
*/
void tray_client::ensure_state() const {
if (!mapped() && ((xembed()->flags & XEMBED_MAPPED) == XEMBED_MAPPED)) {
m_connection.map_window_checked(window());
} else if (mapped() && ((xembed()->flags & XEMBED_MAPPED) != XEMBED_MAPPED)) {
m_connection.unmap_window_checked(window());
}
}
/**
* Configure window size
*/
void tray_client::reconfigure(int16_t x, int16_t y) const {
uint32_t configure_mask = 0;
uint32_t configure_values[7];
xcb_params_configure_window_t configure_params;
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, width, m_width);
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, height, m_height);
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, x, x);
XCB_AUX_ADD_PARAM(&configure_mask, &configure_params, y, y);
xutils::pack_values(configure_mask, &configure_params, configure_values);
m_connection.configure_window_checked(window(), configure_mask, configure_values);
}
/**
* Respond to client resize requests
*/
void tray_client::configure_notify(int16_t x, int16_t y) const {
auto notify = memory_util::make_malloc_ptr<xcb_configure_notify_event_t>(32);
notify->response_type = XCB_CONFIGURE_NOTIFY;
notify->event = m_window;
notify->window = m_window;
notify->override_redirect = false;
notify->above_sibling = 0;
notify->x = x;
notify->y = y;
notify->width = m_width;
notify->height = m_height;
notify->border_width = 0;
const char* data = reinterpret_cast<const char*>(notify.get());
m_connection.send_event_checked(false, m_window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, data);
}
// }}}
// implementation : tray_manager {{{
tray_manager::tray_manager(connection& conn, const logger& logger) : m_connection(conn), m_log(logger) {
m_connection.attach_sink(this, SINK_PRIORITY_TRAY);
}
tray_manager::tray_manager(connection& conn, signal_emitter& emitter, const logger& logger)
: m_connection(conn), m_sig(emitter), m_log(logger) {}
tray_manager::~tray_manager() {
m_connection.detach_sink(this, SINK_PRIORITY_TRAY);
if (m_delaythread.joinable()) {
m_delaythread.join();
}
deactivate();
}
void tray_manager::setup(const bar_settings& bar_opts) {
auto conf = make_confreader();
auto bs = conf.bar_section();
auto tray_position = conf.get<string>(bs, "tray-position", "");
if (tray_position == "left") {
m_opts.align = alignment::LEFT;
} else if (tray_position == "right") {
m_opts.align = alignment::RIGHT;
} else if (tray_position == "center") {
m_opts.align = alignment::CENTER;
} else {
m_opts.align = alignment::NONE;
}
if (m_opts.align == alignment::NONE) {
m_log.warn("Disabling tray manager (reason: position not set or invalid)");
return;
}
m_opts.detached = conf.get<bool>(bs, "tray-detached", false);
m_opts.height = bar_opts.size.h;
m_opts.height -= bar_opts.borders.at(edge::BOTTOM).size;
m_opts.height -= bar_opts.borders.at(edge::TOP).size;
m_opts.height_fill = m_opts.height;
if (m_opts.height % 2 != 0) {
m_opts.height--;
}
auto maxsize = conf.get<int>(bs, "tray-maxsize", 16);
if (m_opts.height > maxsize) {
m_opts.spacing += (m_opts.height - maxsize) / 2;
m_opts.height = maxsize;
}
m_opts.width_max = bar_opts.size.w;
m_opts.width = m_opts.height;
m_opts.orig_y = bar_opts.pos.y + bar_opts.borders.at(edge::TOP).size;
// Apply user-defined scaling
auto scale = conf.get<float>(bs, "tray-scale", 1.0);
m_opts.width *= scale;
m_opts.height_fill *= scale;
auto inner_area = bar_opts.inner_area(true);
switch (m_opts.align) {
case alignment::NONE:
break;
case alignment::LEFT:
m_opts.orig_x = inner_area.x;
break;
case alignment::CENTER:
m_opts.orig_x = inner_area.x + inner_area.width / 2 - m_opts.width / 2;
break;
case alignment::RIGHT:
m_opts.orig_x = inner_area.x + inner_area.width;
break;
}
// Set user-defined background color
if (!(m_opts.transparent = conf.get<bool>(bs, "tray-transparent", m_opts.transparent))) {
auto bg = conf.get<string>(bs, "tray-background", "");
if (bg.length() > 7) {
m_log.warn("Alpha support for the systray is limited. See the wiki for more details.");
}
if (!bg.empty()) {
m_opts.background = color::parse(bg, g_colorempty);
} else {
m_opts.background = bar_opts.background;
}
if (color_util::alpha_channel(m_opts.background) == 0) {
m_opts.transparent = true;
m_opts.background = 0;
}
}
// Add user-defined padding
m_opts.spacing += conf.get<int>(bs, "tray-padding", 0);
// Add user-defiend offset
auto offset_x_def = conf.get<string>(bs, "tray-offset-x", "");
auto offset_y_def = conf.get<string>(bs, "tray-offset-y", "");
auto offset_x = atoi(offset_x_def.c_str());
auto offset_y = atoi(offset_y_def.c_str());
if (offset_x != 0 && offset_x_def.find('%') != string::npos) {
if (m_opts.detached) {
offset_x = math_util::percentage_to_value<int>(offset_x, bar_opts.monitor->w);
} else {
offset_x = math_util::percentage_to_value<int>(offset_x, inner_area.width);
}
}
if (offset_y != 0 && offset_y_def.find('%') != string::npos) {
if (m_opts.detached) {
offset_y = math_util::percentage_to_value<int>(offset_y, bar_opts.monitor->h);
} else {
offset_y = math_util::percentage_to_value<int>(offset_y, inner_area.height);
}
}
m_opts.orig_x += offset_x;
m_opts.orig_y += offset_y;
// Put the tray next to the bar in the window stack
m_opts.sibling = bar_opts.window;
// Activate the tray manager
query_atom();
activate();
}
/**
* Get the settings container
*/
@ -175,17 +178,6 @@ const tray_settings tray_manager::settings() const {
return m_opts;
}
/**
* Initialize data
*/
void tray_manager::bootstrap(tray_settings settings) {
m_opts = settings;
query_atom();
// Listen for visibility change events on the bar window
g_signals::bar::visibility_change = bind(&tray_manager::bar_visibility_change, this, placeholders::_1);
}
/**
* Activate systray management
*/
@ -196,13 +188,17 @@ void tray_manager::activate() {
m_log.info("Activating tray manager");
m_activated = true;
m_opts.running = true;
m_sig.attach(this);
m_connection.attach_sink(this, SINK_PRIORITY_TRAY);
try {
create_window();
create_bg();
restack_window();
set_wmhints();
set_traycolors();
set_wm_hints();
set_tray_colors();
} catch (const exception& err) {
m_log.err(err.what());
m_log.err("Cannot activate tray manager... failed to setup window");
@ -210,6 +206,10 @@ void tray_manager::activate() {
return;
}
// Make sure we receive notificatins when the root pixmap changes
m_connection.ensure_event_mask(m_connection.root(), XCB_EVENT_MASK_PROPERTY_CHANGE);
m_connection.flush();
// Attempt to get control of the systray selection then
// notify clients waiting for a manager.
acquire_selection();
@ -221,8 +221,6 @@ void tray_manager::activate() {
if (m_othermanager != XCB_NONE && m_othermanager != m_tray) {
notify_clients_delayed();
}
m_connection.flush();
}
/**
@ -235,14 +233,10 @@ void tray_manager::deactivate(bool clear_selection) {
m_log.info("Deactivating tray manager");
m_activated = false;
m_opts.running = false;
if (!m_opts.detached) {
g_signals::event::enqueue(eventloop::make(update_event{}, true));
}
if (g_signals::bar::visibility_change) {
g_signals::bar::visibility_change = nullptr;
}
m_sig.detach(this);
m_connection.detach_sink(this, SINK_PRIORITY_TRAY);
if (!m_connection.connection_has_error() && clear_selection && m_acquired_selection) {
m_log.trace("tray: Unset selection owner");
@ -256,11 +250,9 @@ void tray_manager::deactivate(bool clear_selection) {
m_log.trace("tray: Destroy window");
m_connection.destroy_window(m_tray);
}
if (m_pixmap) {
m_connection.free_pixmap(m_pixmap);
}
if (m_gc) {
m_connection.free_gc(m_pixmap);
}
@ -281,6 +273,8 @@ void tray_manager::deactivate(bool clear_selection) {
m_restacked = false;
m_connection.flush();
m_sig.emit(process_update{eventloop::make_update_evt(true)});
}
/**
@ -313,15 +307,13 @@ void tray_manager::reconfigure() {
m_opts.configured_slots = mapped_clients();
if (!m_opts.detached) {
g_signals::event::enqueue(eventloop::make(update_event{}, true));
}
guard.unlock();
refresh_window();
m_connection.flush();
m_sig.emit(process_update{eventloop::make_update_evt(true)});
}
/**
@ -336,7 +328,7 @@ void tray_manager::reconfigure_window() {
auto clients = mapped_clients();
if (!clients && m_mapped && !m_hidden) {
if (!clients && m_mapped) {
m_log.trace("tray: Reconfigure window / unmap");
m_connection.unmap_window_checked(m_tray);
} else if (clients && !m_mapped && !m_hidden) {
@ -352,19 +344,18 @@ void tray_manager::reconfigure_window() {
return;
}
// configure window
if (width > 0) {
m_log.trace("tray: New window values, width=%d, x=%d", width, x);
uint32_t mask = 0;
uint32_t values[7];
xcb_params_configure_window_t params;
m_log.trace("tray: New window values, width=%d, x=%d", width, x);
XCB_AUX_ADD_PARAM(&mask, &params, width, width);
XCB_AUX_ADD_PARAM(&mask, &params, x, x);
xutils::pack_values(mask, &params, values);
m_connection.configure_window_checked(m_tray, mask, values);
}
m_opts.configured_w = width;
m_opts.configured_x = x;
@ -413,9 +404,7 @@ void tray_manager::reconfigure_bg(bool realloc) {
if (realloc && !get_root_pixmap(m_connection, &m_rootpixmap)) {
return m_log.err("Failed to get root pixmap for tray background (realloc=%i)", realloc);
}
if (realloc) {
} else if (realloc) {
// clang-format off
m_log.info("Tray root pixmap (rootpmap=%s, geom=%dx%d+%d+%d, tray=%s, pmap=%s, gc=%s)",
m_connection.id(m_rootpixmap.pixmap),
@ -434,9 +423,8 @@ void tray_manager::reconfigure_bg(bool realloc) {
auto x = calculate_x(w);
auto y = calculate_y();
auto px = m_rootpixmap.x + x;
auto py = m_rootpixmap.y + y;
auto px = math_util::max(0, m_rootpixmap.x + x);
auto py = math_util::max(0, m_rootpixmap.y + y);
// Make sure we don't try to copy void content
if (px + w > m_rootpixmap.width) {
@ -477,16 +465,12 @@ void tray_manager::reconfigure_bg(bool realloc) {
* Refresh the bar window by clearing it along with each client window
*/
void tray_manager::refresh_window() {
if (!m_mtx.try_lock()) {
if (!m_activated || !m_mapped || !m_mtx.try_lock()) {
return;
}
std::lock_guard<std::mutex> lock(m_mtx, std::adopt_lock);
if (!m_activated || !m_mapped) {
return;
}
m_log.trace("tray: Refreshing window");
auto width = calculate_w();
@ -503,6 +487,12 @@ void tray_manager::refresh_window() {
}
m_connection.flush();
if (!mapped_clients()) {
m_opts.configured_w = 0;
} else {
m_opts.configured_w = width;
}
}
/**
@ -547,7 +537,7 @@ void tray_manager::create_window() {
win << cw_params_border_pixel(m_opts.background);
}
m_tray = win << cw_flush();
m_tray = win << cw_flush(true);
m_log.info("Tray window: %s", m_connection.id(m_tray));
xutils::compton_shadow_exclude(m_connection, m_tray);
@ -560,16 +550,13 @@ void tray_manager::create_bg(bool realloc) {
if (!m_opts.transparent) {
return;
}
if (!realloc && m_pixmap && m_gc && m_rootpixmap.pixmap) {
return;
}
if (realloc && m_pixmap) {
m_connection.free_pixmap(m_pixmap);
m_pixmap = 0;
}
if (realloc && m_gc) {
m_connection.free_gc(m_gc);
m_gc = 0;
@ -608,6 +595,7 @@ void tray_manager::restack_window() {
XCB_AUX_ADD_PARAM(&mask, &params, sibling, m_opts.sibling);
XCB_AUX_ADD_PARAM(&mask, &params, stack_mode, XCB_STACK_MODE_ABOVE);
xutils::pack_values(mask, &params, values);
m_connection.configure_window_checked(m_tray, mask, values);
m_restacked = true;
@ -620,34 +608,39 @@ void tray_manager::restack_window() {
/**
* Set window WM hints
*/
void tray_manager::set_wmhints() {
void tray_manager::set_wm_hints() {
const uint32_t visual{m_connection.screen()->root_visual};
const uint32_t orientation{_NET_SYSTEM_TRAY_ORIENTATION_HORZ};
m_log.trace("tray: Set window WM_NAME / WM_CLASS", m_connection.id(m_tray));
xcb_icccm_set_wm_name(m_connection, m_tray, XCB_ATOM_STRING, 8, 19, TRAY_WM_NAME);
xcb_icccm_set_wm_class(m_connection, m_tray, 12, TRAY_WM_CLASS);
m_log.trace("tray: Set window WM_PROTOCOLS");
wm_util::set_wmprotocols(m_connection, m_tray, {WM_DELETE_WINDOW, WM_TAKE_FOCUS});
set_wm_protocols(m_connection, m_tray, {WM_DELETE_WINDOW, WM_TAKE_FOCUS});
m_log.trace("tray: Set window _NET_WM_WINDOW_TYPE");
wm_util::set_windowtype(m_connection, m_tray, {_NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL});
set_wm_window_type(m_connection, m_tray, {_NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL});
m_log.trace("tray: Set window _NET_WM_STATE");
wm_util::set_wmstate(m_connection, m_tray, {_NET_WM_STATE_SKIP_TASKBAR});
set_wm_state(m_connection, m_tray, {_NET_WM_STATE_SKIP_TASKBAR});
m_log.trace("tray: Set window _NET_WM_PID");
wm_util::set_wmpid(m_connection, m_tray, getpid());
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_ORIENTATION");
wm_util::set_trayorientation(m_connection, m_tray, _NET_SYSTEM_TRAY_ORIENTATION_HORZ);
set_wm_pid(m_connection, m_tray, getpid());
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_VISUAL");
wm_util::set_trayvisual(m_connection, m_tray, m_connection.screen()->root_visual);
xcb_change_property(
m_connection, XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_VISUAL, XCB_ATOM_VISUALID, 32, 1, &visual);
m_log.trace("tray: Set window _NET_SYSTEM_TRAY_ORIENTATION");
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_tray, _NET_SYSTEM_TRAY_ORIENTATION,
_NET_SYSTEM_TRAY_ORIENTATION, 32, 1, &orientation);
}
/**
* Set color atom used by clients when determing icon theme
*/
void tray_manager::set_traycolors() {
void tray_manager::set_tray_colors() {
m_log.trace("tray: Set _NET_SYSTEM_TRAY_COLORS to %x", m_opts.background);
auto r = color_util::red_channel(m_opts.background);
@ -669,15 +662,14 @@ void tray_manager::set_traycolors() {
* Acquire the systray selection
*/
void tray_manager::acquire_selection() {
xcb_window_t owner{m_connection.get_selection_owner_unchecked(m_atom)->owner};
auto reply = m_connection.get_selection_owner_unchecked(m_atom);
auto owner = reply.owner<xcb_window_t>();
if (owner == m_tray) {
m_log.info("tray: Already managing the systray selection");
return;
} else if ((m_othermanager = owner) != XCB_NONE) {
m_log.info("Replacing selection manager %s", m_connection.id(owner));
}
} else {
m_log.trace("tray: Change selection owner to %s", m_connection.id(m_tray));
m_connection.set_selection_owner_checked(m_tray, m_atom, XCB_CURRENT_TIME);
@ -687,6 +679,7 @@ void tray_manager::acquire_selection() {
m_acquired_selection = false;
}
}
/**
* Notify pending clients about the new systray MANAGER
@ -709,7 +702,6 @@ void tray_manager::notify_clients_delayed(chrono::duration<double, std::milli> d
if (m_delaythread.joinable()) {
m_delaythread.join();
}
m_delaythread = thread([&] {
this_thread::sleep_for(delay);
notify_clients();
@ -733,17 +725,10 @@ void tray_manager::track_selection_owner(xcb_window_t owner) {
* Process client docking request
*/
void tray_manager::process_docking_request(xcb_window_t win) {
auto client = find_client(win);
if (client) {
m_log.warn("Tray client %s already embedded, ignoring request...", m_connection.id(win));
return;
}
m_log.info("Processing docking request from %s", m_connection.id(win));
m_clients.emplace_back(make_shared<tray_client>(m_connection, win, m_opts.width, m_opts.height));
client = m_clients.back();
m_clients.emplace_back(factory_util::shared<tray_client>(m_connection, win, m_opts.width, m_opts.height));
auto& client = m_clients.back();
try {
m_log.trace("tray: Get client _XEMBED_INFO");
@ -752,19 +737,17 @@ void tray_manager::process_docking_request(xcb_window_t win) {
m_log.err(err.what());
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to query for _XEMBED_INFO, removing client... (%s)", err.what());
remove_client(client, false);
remove_client(win, false);
return;
}
try {
m_log.trace("tray: Update client window");
{
// clang-format off
const uint32_t mask{XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK};
const uint32_t values[]{XCB_BACK_PIXMAP_PARENT_RELATIVE, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
const uint32_t values[]{
XCB_BACK_PIXMAP_PARENT_RELATIVE, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
m_log.trace("tray: Update client window");
m_connection.change_window_attributes_checked(client->window(), mask, values);
// clang-format on
}
m_log.trace("tray: Configure client size");
client->reconfigure(0, 0);
@ -785,44 +768,20 @@ void tray_manager::process_docking_request(xcb_window_t win) {
}
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to setup tray client, removing... (%s)", err.what());
remove_client(client, false);
remove_client(win, false);
}
}
/**
* Signal handler connected to the bar window's visibility change signal.
* This is used as a fallback in case the window restacking fails. It will
* toggle the tray window whenever the visibility of the bar window changes.
*/
void tray_manager::bar_visibility_change(bool visible) {
m_log.trace("tray: visibility_change %d", visible);
m_hidden = !visible;
if (!m_activated || m_restacked) {
return;
} else if (!m_hidden && !m_mapped) {
m_connection.map_window(m_tray);
} else if (m_hidden && m_mapped) {
m_connection.unmap_window(m_tray);
} else if (m_mapped && !m_hidden) {
redraw_window();
}
m_connection.flush();
}
/**
* Calculate x position of tray window
*/
int16_t tray_manager::calculate_x(uint16_t width) const {
auto x = m_opts.orig_x;
if (m_opts.align == alignment::RIGHT) {
x -= ((m_opts.width + m_opts.spacing) * m_clients.size() + m_opts.spacing);
} else if (m_opts.align == alignment::CENTER) {
x -= (width / 2) - (m_opts.width / 2);
}
return x;
}
@ -838,14 +797,14 @@ int16_t tray_manager::calculate_y() const {
*/
uint16_t tray_manager::calculate_w() const {
uint16_t width = m_opts.spacing;
size_t count{0};
for (auto&& client : m_clients) {
if (client->mapped()) {
count++;
width += m_opts.spacing + m_opts.width;
}
}
return width;
return count ? width : 0;
}
/**
@ -874,6 +833,14 @@ int16_t tray_manager::calculate_client_y() {
return (m_opts.height_fill - m_opts.height) / 2;
}
/**
* Check if the given window is embedded
*/
bool tray_manager::is_embedded(const xcb_window_t& win) const {
return m_clients.end() != std::find_if(m_clients.begin(), m_clients.end(),
[win](shared_ptr<tray_client> client) { return client->match(win); });
}
/**
* Find tray client by window
*/
@ -883,14 +850,22 @@ shared_ptr<tray_client> tray_manager::find_client(const xcb_window_t& win) const
return shared_ptr<tray_client>{client.get(), factory_util::null_deleter{}};
}
}
return {};
return nullptr;
}
/**
* Client error handling
* Remove tray client
*/
void tray_manager::remove_client(shared_ptr<tray_client>& client, bool reconfigure) {
m_clients.erase(std::find(m_clients.begin(), m_clients.end(), client));
remove_client(client->window(), reconfigure);
}
/**
* Remove tray client by window
*/
void tray_manager::remove_client(xcb_window_t win, bool reconfigure) {
m_clients.erase(std::remove_if(
m_clients.begin(), m_clients.end(), [win](shared_ptr<tray_client> client) { return client->match(win); }));
if (reconfigure) {
tray_manager::reconfigure();
@ -900,8 +875,8 @@ void tray_manager::remove_client(shared_ptr<tray_client>& client, bool reconfigu
/**
* Get number of mapped clients
*/
int tray_manager::mapped_clients() const {
int mapped_clients = 0;
size_t tray_manager::mapped_clients() const {
size_t mapped_clients = 0;
for (auto&& client : m_clients) {
if (client->mapped()) {
@ -937,21 +912,20 @@ void tray_manager::handle(const evt::visibility_notify& evt) {
void tray_manager::handle(const evt::client_message& evt) {
if (!m_activated) {
return;
}
if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW) {
if (evt->window == m_tray) {
} else if (evt->type == WM_PROTOCOLS && evt->data.data32[0] == WM_DELETE_WINDOW && evt->window == m_tray) {
m_log.warn("Received WM_DELETE");
m_tray = 0;
deactivate();
}
}
if (evt->type == _NET_SYSTEM_TRAY_OPCODE && evt->format == 32) {
} else if (evt->type == _NET_SYSTEM_TRAY_OPCODE && evt->format == 32) {
m_log.trace("tray: Received client_message");
if (SYSTEM_TRAY_REQUEST_DOCK == evt->data.data32[1]) {
if (!is_embedded(evt->data.data32[2])) {
process_docking_request(evt->data.data32[2]);
} else {
auto win = evt->data.data32[2];
m_log.warn("Tray client %s already embedded, ignoring request...", m_connection.id(win));
}
}
}
}
@ -964,22 +938,14 @@ void tray_manager::handle(const evt::client_message& evt) {
* so we return an answer that'll put him in place.
*/
void tray_manager::handle(const evt::configure_request& evt) {
if (!m_activated) {
return;
}
auto client = find_client(evt->window);
if (!client) {
return;
}
if (m_activated && is_embedded(evt->window)) {
try {
m_log.trace("tray: Client configure request %s", m_connection.id(evt->window));
client->configure_notify(calculate_client_x(evt->window), calculate_client_y());
find_client(evt->window)->configure_notify(calculate_client_x(evt->window), calculate_client_y());
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to reconfigure tray client, removing... (%s)", err.what());
remove_client(client);
remove_client(evt->window);
}
}
}
@ -987,22 +953,14 @@ void tray_manager::handle(const evt::configure_request& evt) {
* @see tray_manager::handle(const evt::configure_request&);
*/
void tray_manager::handle(const evt::resize_request& evt) {
if (!m_activated) {
return;
}
auto client = find_client(evt->window);
if (!client) {
return;
}
if (m_activated && is_embedded(evt->window)) {
try {
m_log.trace("tray: Received resize_request for client %s", m_connection.id(evt->window));
client->configure_notify(calculate_client_x(evt->window), calculate_client_y());
find_client(evt->window)->configure_notify(calculate_client_x(evt->window), calculate_client_y());
} catch (const xpp::x::error::window& err) {
m_log.err("Failed to reconfigure tray client, removing... (%s)", err.what());
remove_client(client);
remove_client(evt->window);
}
}
}
@ -1042,7 +1000,10 @@ void tray_manager::handle(const evt::property_notify& evt) {
redraw_window(true);
} else if (evt->atom == ESETROOT_PMAP_ID) {
redraw_window(true);
} else if (evt->atom == _XEMBED_INFO) {
} else if (evt->atom != _XEMBED_INFO) {
return;
}
auto client = find_client(evt->window);
if (!client) {
@ -1066,21 +1027,14 @@ void tray_manager::handle(const evt::property_notify& evt) {
reconfigure();
}
}
}
/**
* Event callback : XCB_REPARENT_NOTIFY
*/
void tray_manager::handle(const evt::reparent_notify& evt) {
if (!m_activated) {
return;
}
auto client = find_client(evt->window);
if (client && evt->parent != m_tray) {
if (m_activated && is_embedded(evt->window) && evt->parent != m_tray) {
m_log.trace("tray: Received reparent_notify for client, remove...");
remove_client(client);
remove_client(evt->window);
}
}
@ -1094,13 +1048,10 @@ void tray_manager::handle(const evt::destroy_notify& evt) {
m_log.trace("tray: Received destroy_notify");
// m_log.info("Tray selection available... re-activating");
// activate();
} else if (m_activated) {
auto client = find_client(evt->window);
if (client) {
} else if (m_activated && is_embedded(evt->window)) {
m_log.trace("tray: Received destroy_notify for client, remove...");
remove_client(client);
}
remove_client(evt->window);
redraw_window();
}
}
@ -1108,22 +1059,15 @@ void tray_manager::handle(const evt::destroy_notify& evt) {
* Event callback : XCB_MAP_NOTIFY
*/
void tray_manager::handle(const evt::map_notify& evt) {
if (!m_activated) {
return;
}
if (evt->window == m_tray) {
if (m_activated && evt->window == m_tray) {
m_log.trace("tray: Received map_notify");
m_log.trace("tray: Update container mapped flag");
m_mapped = true;
} else {
auto client = find_client(evt->window);
if (client) {
redraw_window();
} else if (is_embedded(evt->window)) {
m_log.trace("tray: Received map_notify");
m_log.trace("tray: Set client mapped");
client->mapped(true);
}
find_client(evt->window)->mapped(true);
if (mapped_clients() > m_opts.configured_slots) {
reconfigure();
@ -1135,25 +1079,42 @@ void tray_manager::handle(const evt::map_notify& evt) {
* Event callback : XCB_UNMAP_NOTIFY
*/
void tray_manager::handle(const evt::unmap_notify& evt) {
if (!m_activated) {
return;
}
if (evt->window == m_tray) {
if (m_activated && evt->window == m_tray) {
m_log.trace("tray: Received unmap_notify");
m_log.trace("tray: Update container mapped flag");
m_mapped = false;
} else {
auto client = find_client(evt->window);
if (client) {
} else if (m_activated && is_embedded(evt->window)) {
m_log.trace("tray: Received unmap_notify");
m_log.trace("tray: Set client unmapped");
client->mapped(true);
}
find_client(evt->window)->mapped(true);
}
}
// }}}
/**
* Signal handler connected to the bar window's visibility change signal.
* This is used as a fallback in case the window restacking fails. It will
* toggle the tray window whenever the visibility of the bar window changes.
*/
bool tray_manager::on(const visibility_change& evt) {
bool visible{*evt()};
size_t clients{mapped_clients()};
m_log.trace("tray: visibility_change %d", visible);
m_hidden = !visible;
if (!m_activated || m_restacked) {
return false;
} else if (!m_hidden && !m_mapped && clients) {
m_connection.map_window(m_tray);
} else if ((!clients || m_hidden) && m_mapped) {
m_connection.unmap_window(m_tray);
} else if (m_mapped && !m_hidden && clients) {
redraw_window();
}
m_connection.flush();
return true;
}
POLYBAR_NS_END

View File

@ -7,43 +7,34 @@
POLYBAR_NS
namespace wm_util {
void set_wmname(xcb_connection_t* conn, xcb_window_t win, const string& wm_name, const string& wm_class) {
void set_wm_name(xcb_connection_t* conn, xcb_window_t win, const string& wm_name, const string& wm_class) {
xcb_icccm_set_wm_name(conn, win, XCB_ATOM_STRING, 8, wm_name.length(), wm_name.c_str());
xcb_icccm_set_wm_class(conn, win, wm_class.length(), wm_class.c_str());
}
void set_wmprotocols(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> flags) {
void set_wm_protocols(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> flags) {
xcb_icccm_set_wm_protocols(conn, win, WM_PROTOCOLS, flags.size(), flags.data());
}
void set_windowtype(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> types) {
void set_wm_window_type(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> types) {
xcb_change_property(
conn, XCB_PROP_MODE_REPLACE, win, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, types.size(), types.data());
}
void set_wmstate(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> states) {
void set_wm_state(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> states) {
xcb_change_property(
conn, XCB_PROP_MODE_REPLACE, win, _NET_WM_STATE, XCB_ATOM_ATOM, 32, states.size(), states.data());
}
void set_wmpid(xcb_connection_t* conn, xcb_window_t win, pid_t pid) {
void set_wm_pid(xcb_connection_t* conn, xcb_window_t win, pid_t pid) {
pid = getpid();
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
}
void set_wmdesktop(xcb_connection_t* conn, xcb_window_t win, uint32_t desktop) {
void set_wm_desktop(xcb_connection_t* conn, xcb_window_t win, uint32_t desktop) {
const uint32_t value_list[1]{desktop};
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, value_list);
}
void set_trayorientation(xcb_connection_t* conn, xcb_window_t win, uint32_t orientation) {
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, _NET_SYSTEM_TRAY_ORIENTATION, _NET_SYSTEM_TRAY_ORIENTATION,
32, 1, &orientation);
}
void set_trayvisual(xcb_connection_t* conn, xcb_window_t win, xcb_visualid_t visual) {
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, _NET_SYSTEM_TRAY_VISUAL, XCB_ATOM_VISUALID, 32, 1, &visual);
}
}
POLYBAR_NS_END

View File

@ -1,5 +1,5 @@
#include <X11/Xlib.h>
#include <cstring>
#include <utility>
#include "utils/factory.hpp"
@ -11,9 +11,9 @@ POLYBAR_NS
/**
* Configure injection module
*/
di::injector<const xresource_manager&> configure_xresource_manager() {
auto instance = factory_util::generic_singleton<xresource_manager>();
return di::make_injector(di::bind<>().to(instance));
const xresource_manager& make_xresource_manager() {
auto instance = factory_util::singleton<xresource_manager>();
return static_cast<const xresource_manager&>(*instance);
}
/**

View File

@ -35,14 +35,6 @@ namespace xutils {
return *g_connection_fd.get();
}
uint32_t event_timer_ms(const config& conf, const xcb_button_press_event_t&) {
return conf.get<uint32_t>("settings", "x-delay-buttonpress", 25);
}
uint32_t event_timer_ms(const config& conf, const xcb_randr_notify_event_t&) {
return conf.get<uint32_t>("settings", "x-delay-randrnotify", 50);
}
void pack_values(uint32_t mask, const uint32_t* src, uint32_t* dest) {
for (; mask; mask >>= 1, src++) {
if (mask & 1) {