diff --git a/include/adapters/alsa/control.hpp b/include/adapters/alsa/control.hpp index 27c18091..dcb4d7fd 100644 --- a/include/adapters/alsa/control.hpp +++ b/include/adapters/alsa/control.hpp @@ -29,8 +29,6 @@ namespace alsa { void process_events(); private: - std::mutex m_lock{}; - int m_numid{0}; snd_ctl_t* m_ctl{nullptr}; diff --git a/include/adapters/alsa/mixer.hpp b/include/adapters/alsa/mixer.hpp index b6cc3f42..2f7cc958 100644 --- a/include/adapters/alsa/mixer.hpp +++ b/include/adapters/alsa/mixer.hpp @@ -37,8 +37,6 @@ namespace alsa { bool is_muted(); private: - std::mutex m_lock{}; - snd_mixer_t* m_mixer{nullptr}; snd_mixer_elem_t* m_elem{nullptr}; diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 5db615f9..bb447e47 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -1,41 +1,41 @@ #pragma once +#include + #include "common.hpp" -#include "components/renderer.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" POLYBAR_NS // fwd -class screen; -class tray_manager; +class connection; class logger; +class parser; +class renderer; +class screen; +class signal_emitter; +class tray_manager; -class bar : public xpp::event::sink { +class bar : public xpp::event::sink { public: using make_type = unique_ptr; static make_type make(); explicit bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, - unique_ptr screen, unique_ptr tray_manager); + unique_ptr&& screen, unique_ptr&& tray_manager, unique_ptr&& parser); + virtual ~bar(); - ~bar(); + void parse(string&& data) const; + void parse(const string& data, bool force = false); const bar_settings settings() const; - void parse(const string& data, bool force = false); - protected: void restack_window(); void reconfigure_pos(); @@ -57,10 +57,11 @@ class bar : public xpp::event::sink m_screen; - unique_ptr m_tray; - unique_ptr m_renderer; + unique_ptr m_tray{}; + unique_ptr m_renderer{}; + unique_ptr m_parser{}; - bar_settings m_opts; + bar_settings m_opts{}; string m_lastinput{}; diff --git a/include/components/config.hpp b/include/components/config.hpp index b91d252c..258f2877 100644 --- a/include/components/config.hpp +++ b/include/components/config.hpp @@ -13,7 +13,6 @@ POLYBAR_NS #define GET_CONFIG_VALUE(section, var, name) var = m_conf.get(section, name, var) #define REQ_CONFIG_VALUE(section, var, name) var = m_conf.get(section, name) -#define DEPR_CONFIG_VALUE(section, var, old, name) var = m_conf.deprecated(section, old, name, var) DEFINE_ERROR(value_error); DEFINE_ERROR(key_error); diff --git a/include/components/controller.hpp b/include/components/controller.hpp index 1dd96232..8d1b801a 100644 --- a/include/components/controller.hpp +++ b/include/components/controller.hpp @@ -1,70 +1,69 @@ #pragma once -#include +#include #include "common.hpp" -#include "components/bar.hpp" -#include "components/ipc.hpp" -#include "components/types.hpp" #include "config.hpp" -#include "events/signal_emitter.hpp" #include "events/signal_fwd.hpp" #include "events/signal_receiver.hpp" +#include "events/types.hpp" #include "x11/types.hpp" POLYBAR_NS // fwd decl {{{ +enum class alignment : uint8_t; class bar; +class command; class config; class connection; -class eventloop; +class inotify_watch; +class ipc; class logger; -struct bar_settings; -namespace inotify_util { - class inotify_watch; - using watch_t = unique_ptr; +class signal_emitter; + +namespace modules { + struct module_interface; } -namespace command_util { - class command; -} -using command = command_util::command; -using command_t = unique_ptr; + +using module_t = unique_ptr; +using modulemap_t = std::map>; // }}} -using watch_t = inotify_util::watch_t; +namespace chrono = std::chrono; +using namespace std::chrono_literals; -namespace sig_ev = signals::eventloop; +namespace sig_ev = signals::eventqueue; namespace sig_ui = signals::ui; namespace sig_ipc = signals::ipc; class controller : public signal_receiver { + sig_ev::process_quit, sig_ev::process_check, sig_ipc::process_action, sig_ipc::process_command, + sig_ipc::process_hook, sig_ui::button_press> { public: using make_type = unique_ptr; - static make_type make(string&& path_confwatch, bool enable_ipc = false, bool writeback = false); + static make_type make(unique_ptr&& ipc, unique_ptr&& config_watch); - explicit controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config, - unique_ptr&& eventloop, unique_ptr&& bar, unique_ptr&& ipc, watch_t&& confwatch, bool writeback); + explicit controller(connection&, signal_emitter&, const logger&, const config&, unique_ptr&&, unique_ptr&&, + unique_ptr&&); ~controller(); - void setup(); - bool run(); + bool run(bool writeback = false); - const bar_settings opts() const; + bool enqueue(event&& evt); + bool enqueue(string&& input_data); protected: - void wait_for_signal(); - void wait_for_xevent(); - void wait_for_eventloop(); - void wait_for_configwatch(); + void read_events(); + void process_eventqueue(); + void process_inputdata(); 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_ev::process_check& 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); @@ -75,21 +74,51 @@ class controller : public signal_receiver m_eventloop; unique_ptr m_bar; unique_ptr m_ipc; + unique_ptr m_confwatch; + unique_ptr m_command; - stateflag m_running{false}; - stateflag m_reload{false}; - stateflag m_waiting{false}; - - sigset_t m_waitmask{}; - vector m_threads; - - watch_t m_confwatch; - command_t m_command; - + /** + * @brief Controls weather the output gets printed to stdout + */ bool m_writeback{false}; + + /** + * @brief Internal event queue + */ + using queue_t = moodycamel::BlockingConcurrentQueue; + queue_t m_queue; + + /** + * @brief Loaded modules + */ + modulemap_t m_modules; + + /** + * @brief Maximum number of subsequent events to swallow + */ + size_t m_swallow_limit{5U}; + + /** + * @brief Time to wait for subsequent events + */ + chrono::milliseconds m_swallow_update{10ms}; + + /** + * @brief Time to throttle input events + */ + chrono::milliseconds m_swallow_input{30ms}; + + /** + * @brief Time of last handled input event + */ + chrono::time_point m_lastinput; + + /** + * @brief Input data + */ + string m_inputdata; }; POLYBAR_NS_END diff --git a/include/components/eventloop.hpp b/include/components/eventloop.hpp deleted file mode 100644 index d2751583..00000000 --- a/include/components/eventloop.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -#include "common.hpp" -#include "events/signal_emitter.hpp" -#include "events/signal_fwd.hpp" -#include "events/signal_receiver.hpp" -#include "utils/concurrency.hpp" - -POLYBAR_NS - -// fwd -namespace modules { - struct module_interface; -} -enum class alignment : uint8_t; -class config; -class logger; - -using namespace signals::eventloop; - -using module_t = unique_ptr; -using modulemap_t = std::map>; - -namespace chrono = std::chrono; -using namespace std::chrono_literals; - -class eventloop : public signal_receiver { - public: - enum class event_type : uint8_t { - NONE = 0, - UPDATE, - CHECK, - INPUT, - QUIT, - }; - - struct event { - uint8_t type{static_cast(event_type::NONE)}; - bool flag{false}; - }; - - template - using queue_t = moodycamel::BlockingConcurrentQueue; - - public: - using make_type = unique_ptr; - static make_type make(); - - explicit eventloop(signal_emitter& emitter, const logger& logger, const config& config); - ~eventloop(); - - void start(); - void stop(); - - bool enqueue(event&& evt); - bool enqueue(string&& input_data); - - void add_module(const alignment pos, module_t&& module); - const modulemap_t& modules() const; - size_t module_count() const; - - static event make_quit_evt(bool reload = false); - static event make_update_evt(bool force = false); - static event make_input_evt(); - static event make_check_evt(); - - protected: - void dispatch_modules(); - - void handle_inputdata(); - - 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; - - /** - * @brief Loaded modules - */ - modulemap_t m_modules; - - /** - * @brief Flag to indicate current run state - */ - stateflag m_running{}; - - /** - * @brief Time to wait for subsequent events - */ - chrono::milliseconds m_swallow_update{10ms}; - - /** - * @brief Maximum amount of subsequent events to swallow within timeframe - */ - size_t m_swallow_limit{5U}; - - /** - * @brief Time to throttle input events - */ - chrono::milliseconds m_swallow_input{30ms}; - - /** - * @brief Time of last handled input event - */ - chrono::time_point m_lastinput; - - /** - * @brief Mutex used to guard input data - */ - std::mutex m_inputlock{}; - - /** - * @brief Input data - */ - string m_inputdata; -}; - -POLYBAR_NS_END diff --git a/include/components/ipc.hpp b/include/components/ipc.hpp index 201c97c6..8180fb09 100644 --- a/include/components/ipc.hpp +++ b/include/components/ipc.hpp @@ -5,6 +5,7 @@ #include "events/signal_emitter.hpp" #include "utils/concurrency.hpp" #include "utils/functional.hpp" +#include "utils/file.hpp" POLYBAR_NS @@ -36,20 +37,18 @@ class ipc { using make_type = unique_ptr; static make_type make(); - explicit ipc(signal_emitter& emitter, const logger& logger) : m_sig(emitter), m_log(logger) {} + explicit ipc(signal_emitter& emitter, const logger& logger); ~ipc(); - void receive_messages(); - - protected: - void parse(const string& payload) const; + void receive_message(); + int get_file_descriptor() const; private: signal_emitter& m_sig; const logger& m_log; - string m_fifo{}; - int m_fd{0}; - stateflag m_running{false}; + + string m_path{}; + shared_ptr m_fd{}; }; POLYBAR_NS_END diff --git a/include/components/parser.hpp b/include/components/parser.hpp index ea10488e..b3e62d61 100644 --- a/include/components/parser.hpp +++ b/include/components/parser.hpp @@ -6,9 +6,9 @@ POLYBAR_NS class signal_emitter; -struct bar_settings; enum class attribute : uint8_t; enum class mousebtn : uint8_t; +struct bar_settings; DEFINE_ERROR(parser_error); DEFINE_CHILD_ERROR(unrecognized_token, parser_error); @@ -21,12 +21,15 @@ class parser { uint16_t data[128]{0U}; size_t length{0}; }; + using make_type = unique_ptr; + static make_type make(); - explicit parser(signal_emitter& emitter, const bar_settings& bar); - void operator()(string data); + public: + explicit parser(signal_emitter& emitter); + void parse(const bar_settings& bar, string data); protected: - void codeblock(string&& data); + void codeblock(string&& data, const bar_settings& bar); size_t text(string&& data); uint32_t parse_color(const string& s, uint32_t fallback = 0); @@ -37,8 +40,8 @@ class parser { private: signal_emitter& m_sig; - const bar_settings& m_bar; vector m_actions; + unique_ptr m_parser; }; POLYBAR_NS_END diff --git a/include/config.hpp.cmake b/include/config.hpp.cmake index 6b94aa31..9b7ab46e 100644 --- a/include/config.hpp.cmake +++ b/include/config.hpp.cmake @@ -32,12 +32,11 @@ 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 SIGN_PRIORITY_CONTROLLER{1}; +static const int SIGN_PRIORITY_SCREEN{2}; +static const int SIGN_PRIORITY_BAR{3}; +static const int SIGN_PRIORITY_RENDERER{4}; +static const int SIGN_PRIORITY_TRAY{5}; static const int SINK_PRIORITY_BAR{1}; static const int SINK_PRIORITY_SCREEN{2}; diff --git a/include/errors.hpp b/include/errors.hpp index 1c1da890..9fb3df55 100644 --- a/include/errors.hpp +++ b/include/errors.hpp @@ -14,6 +14,7 @@ using std::runtime_error; class application_error : public runtime_error { public: explicit application_error(const string& message, int code = 0) : runtime_error(message), code(code) {} + virtual ~application_error() {} int code{0}; }; @@ -22,6 +23,7 @@ class system_error : public application_error { explicit system_error() : application_error(strerror(errno), errno) {} explicit system_error(const string& message) : application_error(message + " (reason: " + strerror(errno) + ")", errno) {} + virtual ~system_error() {} }; #define DEFINE_CHILD_ERROR(error, parent) \ diff --git a/include/events/signal.hpp b/include/events/signal.hpp index a96c8286..602eb8f9 100644 --- a/include/events/signal.hpp +++ b/include/events/signal.hpp @@ -2,10 +2,10 @@ #include "common.hpp" -#include "components/eventloop.hpp" #include "components/ipc.hpp" #include "components/parser.hpp" #include "components/types.hpp" +#include "events/types.hpp" #include "utils/functional.hpp" POLYBAR_NS @@ -91,19 +91,11 @@ namespace signals { // }}} } - 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); + namespace eventqueue { + DEFINE_VALUE_SIGNAL(1, process_quit, event); + DEFINE_VALUE_SIGNAL(2, process_update, event); DEFINE_VALUE_SIGNAL(3, process_input, string); - DEFINE_VALUE_SIGNAL(4, process_check, 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, string); - DEFINE_VALUE_SIGNAL(9, enqueue_check, eventloop_t::event); + DEFINE_VALUE_SIGNAL(4, process_check, event); } namespace ipc { diff --git a/include/events/signal_fwd.hpp b/include/events/signal_fwd.hpp index 25051983..ca628bef 100644 --- a/include/events/signal_fwd.hpp +++ b/include/events/signal_fwd.hpp @@ -5,17 +5,11 @@ POLYBAR_NS namespace signals { - namespace eventloop { + namespace eventqueue { 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; diff --git a/include/events/types.hpp b/include/events/types.hpp new file mode 100644 index 00000000..3e091c70 --- /dev/null +++ b/include/events/types.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +enum class event_type : uint8_t { + NONE = 0, + UPDATE, + CHECK, + INPUT, + QUIT, +}; + +struct event { + uint8_t type{0}; + bool flag{false}; +}; + +namespace { + /** + * Create QUIT event + */ + inline event make_quit_evt(bool reload = false) { + return event{static_cast(event_type::QUIT), reload}; + } + + /** + * Create UPDATE event + */ + inline event make_update_evt(bool force = false) { + return event{static_cast(event_type::UPDATE), force}; + } + + /** + * Create INPUT event + */ + inline event make_input_evt() { + return event{static_cast(event_type::INPUT)}; + } + + /** + * Create CHECK event + */ + inline event make_check_evt() { + return event{static_cast(event_type::CHECK)}; + } +} diff --git a/include/modules/meta/inotify_module.inl b/include/modules/meta/inotify_module.inl index 7f61f0a5..91d371f0 100644 --- a/include/modules/meta/inotify_module.inl +++ b/include/modules/meta/inotify_module.inl @@ -43,7 +43,7 @@ namespace modules { template void inotify_module::poll_events() { - vector watches; + vector> watches; try { for (auto&& w : m_watchlist) { diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index 967a0c94..dbc7dc73 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -1,11 +1,9 @@ #pragma once #include -#include #include "adapters/mpd.hpp" #include "modules/meta/event_module.hpp" -#include "utils/concurrency.hpp" POLYBAR_NS diff --git a/include/utils/command.hpp b/include/utils/command.hpp index 1fd59acb..17fe543a 100644 --- a/include/utils/command.hpp +++ b/include/utils/command.hpp @@ -1,87 +1,84 @@ #pragma once +#include + #include "common.hpp" #include "components/logger.hpp" #include "errors.hpp" -#include "utils/concurrency.hpp" #include "utils/factory.hpp" #include "utils/functional.hpp" POLYBAR_NS +DEFINE_ERROR(command_error); + +/** + * Wrapper used to execute command in a subprocess. + * In-/output streams are opened to enable ipc. + * + * Example usage: + * + * @code cpp + * auto cmd = command_util::make_command("cat /etc/rc.local"); + * cmd->exec(); + * cmd->tail([](string s) { std::cout << s << std::endl; }); + * @endcode + * + * @code cpp + * auto cmd = command_util::make_command( + * "while read -r line; do echo data from parent process: $line; done"); + * cmd->exec(false); + * cmd->writeline("Test"); + * cout << cmd->readline(); + * cmd->wait(); + * @endcode + * + * @code cpp + * auto cmd = command_util::make_command("for i in 1 2 3; do echo $i; done"); + * cmd->exec(); + * cout << cmd->readline(); // 1 + * cout << cmd->readline() << cmd->readline(); // 23 + * @endcode + */ +class command { + public: + explicit command(const logger& logger, string cmd); + + ~command(); + + int exec(bool wait_for_completion = true); + void terminate(); + bool is_running(); + int wait(); + + void tail(callback cb); + int writeline(string data); + string readline(); + + int get_stdout(int c); + int get_stdin(int c); + pid_t get_pid(); + int get_exit_status(); + + protected: + const logger& m_log; + + string m_cmd; + + int m_stdout[2]{}; + int m_stdin[2]{}; + + pid_t m_forkpid{}; + int m_forkstatus{}; + + std::mutex m_pipelock; +}; + namespace command_util { - DEFINE_ERROR(command_error); - DEFINE_CHILD_ERROR(command_strerror, command_error); - - /** - * Wrapper used to execute command in a subprocess. - * In-/output streams are opened to enable ipc. - * - * Example usage: - * - * @code cpp - * auto cmd = command_util::make_command("cat /etc/rc.local"); - * cmd->exec(); - * cmd->tail([](string s) { std::cout << s << std::endl; }); - * @endcode - * - * @code cpp - * auto cmd = command_util::make_command( - * "while read -r line; do echo data from parent process: $line; done"); - * cmd->exec(false); - * cmd->writeline("Test"); - * cout << cmd->readline(); - * cmd->wait(); - * @endcode - * - * @code cpp - * auto cmd = command_util::make_command("for i in 1 2 3; do echo $i; done"); - * cmd->exec(); - * cout << cmd->readline(); // 1 - * cout << cmd->readline() << cmd->readline(); // 23 - * @endcode - */ - class command { - public: - explicit command(const logger& logger, string cmd); - - ~command(); - - int exec(bool wait_for_completion = true); - void terminate(); - bool is_running(); - int wait(); - - void tail(callback cb); - int writeline(string data); - string readline(); - - int get_stdout(int c); - int get_stdin(int c); - pid_t get_pid(); - int get_exit_status(); - - protected: - const logger& m_log; - - string m_cmd; - - int m_stdout[2]{}; - int m_stdin[2]{}; - - pid_t m_forkpid{}; - int m_forkstatus{}; - - concurrency_util::spin_lock m_pipelock; - }; - template unique_ptr make_command(Args&&... args) { return factory_util::unique(logger::make(), forward(args)...); } } -using command = command_util::command; -using command_t = unique_ptr; - POLYBAR_NS_END diff --git a/include/utils/factory.hpp b/include/utils/factory.hpp index 6db4185d..6e0bbacc 100644 --- a/include/utils/factory.hpp +++ b/include/utils/factory.hpp @@ -7,18 +7,23 @@ POLYBAR_NS namespace factory_util { - struct null_deleter { - template - void operator()(T*) const {} - }; + namespace detail { + struct null_deleter { + template + void operator()(T*) const {} + }; - struct fd_deleter { - void operator()(int* fd) const { - if (fd != nullptr && *fd > 0) { - close(*fd); + struct fd_deleter { + void operator()(int* fd) const { + if (fd != nullptr && *fd > 0) { + close(*fd); + } } - } - }; + }; + } + + extern detail::null_deleter null_deleter; + extern detail::fd_deleter fd_deleter; template unique_ptr unique(Deps&&... deps) { diff --git a/include/utils/file.hpp b/include/utils/file.hpp index fcf5594a..b0531f99 100644 --- a/include/utils/file.hpp +++ b/include/utils/file.hpp @@ -1,39 +1,50 @@ #pragma once #include "common.hpp" +#include "utils/factory.hpp" POLYBAR_NS +class file_ptr { + public: + explicit file_ptr(const string& path, const string& mode = "a+"); + ~file_ptr(); + + file_ptr(const file_ptr& o) = delete; + file_ptr& operator=(const file_ptr& o) = delete; + + operator bool(); + + FILE* operator()(); + + protected: + FILE* m_ptr = nullptr; + string m_path; + string m_mode; +}; + +class file_descriptor { + public: + explicit file_descriptor(const string& path, int flags = 0); + explicit file_descriptor(int fd); + ~file_descriptor(); + operator int(); + + protected: + int m_fd{0}; +}; + namespace file_util { - /** - * RAII file wrapper - */ - class file_ptr { - public: - explicit file_ptr(const string& path, const string& mode = "a+") : m_path(string(path)), m_mode(string(mode)) { - m_ptr = fopen(m_path.c_str(), m_mode.c_str()); - } - - ~file_ptr(); - - file_ptr(const file_ptr& o) = delete; - file_ptr& operator=(const file_ptr& o) = delete; - - operator bool(); - - FILE* operator()(); - - protected: - FILE* m_ptr = nullptr; - string m_path; - string m_mode; - }; - bool exists(const string& filename); string get_contents(const string& filename); void set_block(int fd); void set_nonblock(int fd); bool is_fifo(string filename); + + template + decltype(auto) make_file_descriptor(Args&&... args) { + return factory_util::shared(forward(args)...); + } } POLYBAR_NS_END diff --git a/include/utils/inotify.hpp b/include/utils/inotify.hpp index 36e697ae..b423143e 100644 --- a/include/utils/inotify.hpp +++ b/include/utils/inotify.hpp @@ -5,6 +5,7 @@ #include #include "common.hpp" +#include "utils/factory.hpp" POLYBAR_NS @@ -16,33 +17,33 @@ struct inotify_event { int mask = 0; }; +class inotify_watch { + public: + explicit inotify_watch(string path); + ~inotify_watch(); + + void attach(int mask = IN_MODIFY); + void remove(bool force = false); + bool poll(int wait_ms = 1000); + unique_ptr get_event(); + bool await_match(); + const string path() const; + int get_file_descriptor() const; + + protected: + string m_path; + int m_fd = -1; + int m_wd = -1; + int m_mask = 0; +}; + namespace inotify_util { - using event_t = inotify_event; + bool match(const inotify_event* evt, int mask); - class inotify_watch { - public: - explicit inotify_watch(string path); - ~inotify_watch() noexcept; - - void attach(int mask = IN_MODIFY); - void remove(bool force = false); - bool poll(int wait_ms = 1000); - unique_ptr get_event(); - bool await_match(); - const string path() const; - - protected: - string m_path; - int m_fd = -1; - int m_wd = -1; - int m_mask = 0; - }; - - using watch_t = unique_ptr; - - watch_t make_watch(string path); - - bool match(const event_t* evt, int mask); + template + decltype(auto) make_watch(Args&&... args) { + return factory_util::unique(forward(args)...); + } } POLYBAR_NS_END diff --git a/include/utils/time.hpp b/include/utils/time.hpp index a31c3ddf..3cf57260 100644 --- a/include/utils/time.hpp +++ b/include/utils/time.hpp @@ -11,12 +11,12 @@ namespace chrono = std::chrono; namespace time_util { using clock_t = chrono::high_resolution_clock; - template + template auto measure(const T& expr) noexcept { auto start = clock_t::now(); expr(); auto finish = clock_t::now(); - return chrono::duration_cast(finish - start).count(); + return chrono::duration_cast(finish - start).count(); } } diff --git a/include/x11/connection.hpp b/include/x11/connection.hpp index 42583f7a..4f581698 100644 --- a/include/x11/connection.hpp +++ b/include/x11/connection.hpp @@ -71,8 +71,6 @@ class connection : public xpp_connection { void send_client_message(const shared_ptr& message, xcb_window_t target, uint32_t event_mask = 0xFFFFFF, bool propagate = false) const; - void send_dummy_event(xcb_window_t target, uint32_t event = XCB_EVENT_MASK_STRUCTURE_NOTIFY) const; - xcb_visualtype_t* visual_type(xcb_screen_t* screen, int match_depth = 32); static string error_str(int error_code); diff --git a/include/x11/tray_manager.hpp b/include/x11/tray_manager.hpp index 6e26b6f2..e44768b0 100644 --- a/include/x11/tray_manager.hpp +++ b/include/x11/tray_manager.hpp @@ -29,7 +29,7 @@ POLYBAR_NS namespace chrono = std::chrono; using namespace std::chrono_literals; -using namespace signals::eventloop; +using namespace signals::eventqueue; using namespace signals::ui; // fwd declarations diff --git a/include/x11/wm.hpp b/include/x11/wm.hpp index 64b16df9..cb6faf06 100644 --- a/include/x11/wm.hpp +++ b/include/x11/wm.hpp @@ -13,7 +13,7 @@ namespace wm_util { void set_wm_state(xcb_connection_t* conn, xcb_window_t win, vector 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); - void set_wm_window_opacity(xcb_connection_t* conn, xcb_window_t win, uint64_t value); + void set_wm_window_opacity(xcb_connection_t* conn, xcb_window_t win, uint64_t values); } POLYBAR_NS_END diff --git a/include/x11/xutils.hpp b/include/x11/xutils.hpp index 2458e574..5f495a3d 100644 --- a/include/x11/xutils.hpp +++ b/include/x11/xutils.hpp @@ -13,6 +13,12 @@ class connection; class config; namespace xutils { + struct xcb_connection_deleter { + void operator()(xcb_connection_t* c) { + xcb_disconnect(c); + } + }; + shared_ptr get_connection(); int get_connection_fd(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 004b9f10..34e24462 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,46 +2,8 @@ # Create executable # -# Generate source tree {{{ - file(GLOB_RECURSE SOURCES RELATIVE ${PROJECT_SOURCE_DIR}/src *.c[p]*) -if(NOT ENABLE_ALSA) - list(REMOVE_ITEM SOURCES adapters/alsa/mixer.cpp adapters/alsa/control.cpp modules/volume.cpp) -endif() -if(NOT ENABLE_MPD) - list(REMOVE_ITEM SOURCES adapters/mpd.cpp modules/mpd.cpp) -endif() -if(NOT ENABLE_NETWORK) - list(REMOVE_ITEM SOURCES adapters/net.cpp modules/network.cpp) -endif() -if(NOT ENABLE_I3) - list(REMOVE_ITEM SOURCES modules/i3.cpp utils/i3.cpp) -endif() -if(NOT ENABLE_CURL) - list(REMOVE_ITEM SOURCES utils/http.cpp modules/github.cpp) -endif() - -if(NOT WITH_XCOMPOSITE) - list(REMOVE_ITEM SOURCES x11/composite.cpp) -endif() -if(NOT WITH_XDAMAGE) - list(REMOVE_ITEM SOURCES x11/damage.cpp) -endif() -if(NOT WITH_XRANDR) - list(REMOVE_ITEM SOURCES x11/randr.cpp) -endif() -if(NOT WITH_XRENDER) - list(REMOVE_ITEM SOURCES x11/render.cpp) -endif() -if(NOT WITH_XSYNC) - list(REMOVE_ITEM SOURCES x11/sync.cpp) -endif() -if(NOT WITH_XKB) - list(REMOVE_ITEM SOURCES x11/xkb.cpp modules/xkeyboard.cpp) -endif() - -# }}} # Locate dependencies {{{ set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -65,26 +27,38 @@ set(XCB_PROTOS xproto) if(WITH_XRANDR) set(XCB_PROTOS "${XCB_PROTOS}" randr) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::randr::extension) +else() + list(REMOVE_ITEM SOURCES x11/randr.cpp) endif() if(WITH_XRENDER) set(XCB_PROTOS "${XCB_PROTOS}" render) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::render::extension) +else() + list(REMOVE_ITEM SOURCES x11/render.cpp) endif() if(WITH_XDAMAGE) set(XCB_PROTOS "${XCB_PROTOS}" damage) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::damage::extension) +else() + list(REMOVE_ITEM SOURCES x11/damage.cpp) endif() if(WITH_XSYNC) set(XCB_PROTOS "${XCB_PROTOS}" sync) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::sync::extension) +else() + list(REMOVE_ITEM SOURCES x11/sync.cpp) endif() if(WITH_XCOMPOSITE) set(XCB_PROTOS "${XCB_PROTOS}" composite) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::composite::extension) +else() + list(REMOVE_ITEM SOURCES x11/composite.cpp) endif() if(WITH_XKB) set(XCB_PROTOS "${XCB_PROTOS}" xkb) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::xkb::extension) +else() + list(REMOVE_ITEM SOURCES x11/xkb.cpp modules/xkeyboard.cpp) endif() string(REPLACE ";" ", " XPP_EXTENSION_LIST "${XPP_EXTENSION_LIST}") @@ -101,6 +75,8 @@ if(ENABLE_ALSA) find_package(ALSA REQUIRED) set(APP_LIBRARIES ${APP_LIBRARIES} ${ALSA_LIBRARY}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${ALSA_INCLUDE_DIR}) +else() + list(REMOVE_ITEM SOURCES adapters/alsa/mixer.cpp adapters/alsa/control.cpp modules/volume.cpp) endif() # }}} @@ -110,6 +86,8 @@ if(ENABLE_MPD) find_package(LibMPDClient REQUIRED) set(APP_LIBRARIES ${APP_LIBRARIES} ${LIBMPDCLIENT_LIBRARY}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${LIBMPDCLIENT_INCLUDE_DIR}) +else() + list(REMOVE_ITEM SOURCES adapters/mpd.cpp modules/mpd.cpp) endif() # }}} @@ -119,6 +97,8 @@ if(ENABLE_NETWORK) find_package(Libiw REQUIRED) set(APP_LIBRARIES ${APP_LIBRARIES} ${LIBIW_LIBRARY}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${LIBIW_INCLUDE_DIR}) +else() + list(REMOVE_ITEM SOURCES adapters/net.cpp modules/network.cpp) endif() # }}} @@ -128,15 +108,19 @@ if(ENABLE_I3) add_subdirectory(${PROJECT_SOURCE_DIR}/lib/i3ipcpp ${PROJECT_BINARY_DIR}/lib/i3ipcpp) set(APP_LIBRARIES ${APP_LIBRARIES} ${I3IPCPP_LIBRARIES}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${I3IPCPP_INCLUDE_DIRS}) +else() + list(REMOVE_ITEM SOURCES modules/i3.cpp utils/i3.cpp) endif() # }}} -# Optional dependency: i3ipcpp {{{ +# Optional dependency: libcurl {{{ if(ENABLE_CURL) find_package(CURL REQUIRED) set(APP_LIBRARIES ${APP_LIBRARIES} ${CURL_LIBRARY}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CURL_INCLUDE_DIR}) +else() + list(REMOVE_ITEM SOURCES utils/http.cpp modules/github.cpp) endif() # }}} diff --git a/src/adapters/alsa/control.cpp b/src/adapters/alsa/control.cpp index fe78a412..3d7d3a48 100644 --- a/src/adapters/alsa/control.cpp +++ b/src/adapters/alsa/control.cpp @@ -54,13 +54,9 @@ namespace alsa { * Deconstruct control object */ control::~control() { - std::lock_guard guard(m_lock); - if (m_hctl != nullptr) { snd_hctl_close(m_hctl); } - - snd_config_update_free_global(); } /** @@ -76,12 +72,6 @@ namespace alsa { bool control::wait(int timeout) { assert(m_ctl); - if (!m_lock.try_lock()) { - return false; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - int err{0}; if ((err = snd_ctl_wait(m_ctl, timeout)) == -1) { @@ -108,12 +98,6 @@ namespace alsa { bool control::test_device_plugged() { assert(m_elem); - if (!m_lock.try_lock()) { - return false; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - snd_ctl_elem_value_t* m_value{nullptr}; snd_ctl_elem_value_alloca(&m_value); diff --git a/src/adapters/alsa/mixer.cpp b/src/adapters/alsa/mixer.cpp index ce41afbd..aec3cd7a 100644 --- a/src/adapters/alsa/mixer.cpp +++ b/src/adapters/alsa/mixer.cpp @@ -45,8 +45,6 @@ namespace alsa { * Deconstruct mixer */ mixer::~mixer() { - std::lock_guard guard(m_lock); - if (m_mixer != nullptr) { snd_mixer_close(m_mixer); } @@ -65,20 +63,12 @@ namespace alsa { bool mixer::wait(int timeout) { assert(m_mixer); - if (!m_lock.try_lock()) { - return false; - } - - std::unique_lock guard(m_lock, std::adopt_lock); - int err = 0; if ((err = snd_mixer_wait(m_mixer, timeout)) == -1) { throw_exception("Failed to wait for events", err); } - guard.unlock(); - return process_events() > 0; } @@ -86,12 +76,6 @@ namespace alsa { * Process queued mixer events */ int mixer::process_events() { - if (!m_lock.try_lock()) { - return false; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - int num_events{0}; if ((num_events = snd_mixer_handle_events(m_mixer)) == -1) { throw_exception("Failed to process pending events", num_events); @@ -106,12 +90,6 @@ namespace alsa { int mixer::get_volume() { assert(m_elem != nullptr); - if (!m_lock.try_lock()) { - return 0; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - long chan_n = 0, vol_total = 0, vol, vol_min, vol_max; snd_mixer_selem_get_playback_volume_range(m_elem, &vol_min, &vol_max); @@ -133,12 +111,6 @@ namespace alsa { int mixer::get_normalized_volume() { assert(m_elem != nullptr); - if (!m_lock.try_lock()) { - return 0; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - long chan_n = 0, vol_total = 0, vol, vol_min, vol_max; double normalized, min_norm; @@ -175,12 +147,6 @@ namespace alsa { return; } - if (!m_lock.try_lock()) { - return; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - long vol_min, vol_max; snd_mixer_selem_get_playback_volume_range(m_elem, &vol_min, &vol_max); snd_mixer_selem_set_playback_volume_all(m_elem, math_util::percentage_to_value(percentage, vol_min, vol_max)); @@ -196,12 +162,6 @@ namespace alsa { return; } - if (!m_lock.try_lock()) { - return; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - long vol_min, vol_max; double min_norm; percentage = percentage / 100.0f; @@ -227,12 +187,6 @@ namespace alsa { void mixer::set_mute(bool mode) { assert(m_elem != nullptr); - if (!m_lock.try_lock()) { - return; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - snd_mixer_selem_set_playback_switch_all(m_elem, mode); } @@ -242,12 +196,6 @@ namespace alsa { void mixer::toggle_mute() { assert(m_elem != nullptr); - if (!m_lock.try_lock()) { - return; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - int state; snd_mixer_selem_get_playback_switch(m_elem, SND_MIXER_SCHN_MONO, &state); @@ -260,12 +208,6 @@ namespace alsa { bool mixer::is_muted() { assert(m_elem != nullptr); - if (!m_lock.try_lock()) { - return false; - } - - std::lock_guard guard(m_lock, std::adopt_lock); - int state = 0; for (int i = 0; i <= SND_MIXER_SCHN_LAST; i++) { diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 5d25cfa6..1a7e98a6 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -5,6 +5,7 @@ #include "components/bar.hpp" #include "components/config.hpp" #include "components/parser.hpp" +#include "components/renderer.hpp" #include "components/screen.hpp" #include "events/signal.hpp" #include "events/signal_emitter.hpp" @@ -40,7 +41,8 @@ bar::make_type bar::make() { config::make(), logger::make(), screen::make(), - tray_manager::make()); + tray_manager::make(), + parser::make()); // clang-format on } @@ -50,13 +52,14 @@ bar::make_type bar::make() { * TODO: Break out all tray handling */ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, - unique_ptr screen, unique_ptr tray_manager) + unique_ptr&& screen, unique_ptr&& tray_manager, unique_ptr&& parser) : m_connection(conn) , m_sig(emitter) , m_conf(config) , m_log(logger) - , m_screen(move(screen)) - , m_tray(move(tray_manager)) { + , m_screen(forward(screen)) + , m_tray(forward(tray_manager)) + , m_parser(forward(parser)) { string bs{m_conf.section()}; // Get available RandR outputs @@ -69,6 +72,14 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const throw application_error("No monitors found"); } + if (monitor_name.empty() && !monitor_strictmode) { + auto connected_monitors = randr_util::get_monitors(m_connection, m_connection.screen()->root, true); + if (!connected_monitors.empty()) { + monitor_name = connected_monitors[0]->name; + m_log.warn("No monitor specified, using \"%s\"", monitor_name); + } + } + if (monitor_name.empty()) { monitor_name = monitors[0]->name; m_log.warn("No monitor specified, using \"%s\"", monitor_name); @@ -99,7 +110,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const } } - m_log.trace("bar: Loaded monitor %s (%ix%i+%i+%i)", m_opts.monitor->name, m_opts.monitor->w, m_opts.monitor->h, + m_log.info("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 { @@ -316,10 +327,7 @@ void bar::parse(const string& data, bool force) { m_renderer->fill_background(); try { - if (!data.empty()) { - parser parser{m_sig, m_opts}; - parser(data); - } + m_parser->parse(settings(), data); } catch (const parser_error& err) { m_log.err("Failed to parse contents (reason: %s)", err.what()); } diff --git a/src/components/controller.cpp b/src/components/controller.cpp index e62cff66..98f18e67 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -1,266 +1,348 @@ -#include "components/controller.hpp" -#include "common.hpp" +#include + #include "components/bar.hpp" #include "components/config.hpp" -#include "components/eventloop.hpp" +#include "components/controller.hpp" #include "components/ipc.hpp" #include "components/logger.hpp" +#include "components/renderer.hpp" +#include "components/types.hpp" #include "events/signal.hpp" +#include "events/signal_emitter.hpp" #include "modules/meta/factory.hpp" #include "utils/command.hpp" #include "utils/factory.hpp" #include "utils/inotify.hpp" #include "utils/process.hpp" #include "utils/string.hpp" +#include "utils/time.hpp" #include "x11/connection.hpp" +#include "x11/tray_manager.hpp" #include "x11/xutils.hpp" POLYBAR_NS -using namespace modules; +int g_eventpipe[2]{0, 0}; +sig_atomic_t g_reload{0}; +sig_atomic_t g_terminate{0}; -/** - * Create instance - */ -controller::make_type controller::make(string&& path_confwatch, bool enable_ipc, bool writeback) { - // clang-format off - return factory_util::unique( - connection::make(), - signal_emitter::make(), - logger::make(), - config::make(), - eventloop::make(), - bar::make(), - enable_ipc ? ipc::make() : ipc::make_type{}, - !path_confwatch.empty() ? inotify_util::make_watch(forward(move(path_confwatch))) : watch_t{}, - writeback); - // clang-format on +void interrupt_handler(int signum) { + g_terminate = 1; + g_reload = (signum == SIGUSR1); + write(g_eventpipe[PIPE_WRITE], &g_terminate, 1); } /** - * Construct controller object + * Build controller instance + */ +controller::make_type controller::make(unique_ptr&& ipc, unique_ptr&& config_watch) { + return factory_util::unique(connection::make(), signal_emitter::make(), logger::make(), config::make(), + bar::make(), forward(ipc), forward(config_watch)); +} + +/** + * Construct controller */ controller::controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config, - unique_ptr&& eventloop, unique_ptr&& bar, unique_ptr&& ipc, watch_t&& confwatch, - bool writeback) + unique_ptr&& bar, unique_ptr&& ipc, unique_ptr&& confwatch) : m_connection(conn) , m_sig(emitter) , m_log(logger) , m_conf(config) - , m_eventloop(forward(eventloop)) , m_bar(forward(bar)) , m_ipc(forward(ipc)) - , m_confwatch(forward(confwatch)) - , m_writeback(writeback) {} + , m_confwatch(forward(confwatch)) { + m_swallow_input = m_conf.get("settings", "throttle-input-for", m_swallow_input); + m_swallow_limit = m_conf.deprecated("settings", "eventqueue-swallow", "throttle-output", m_swallow_limit); + m_swallow_update = m_conf.deprecated("settings", "eventqueue-swallow-time", "throttle-output-for", m_swallow_update); -/** - * Deconstruct controller object - */ -controller::~controller() { - if (m_command) { - m_log.info("Terminating running shell command"); - m_command.reset(); - } - if (!m_writeback) { - m_log.info("Interrupting X event loop"); - m_connection.send_dummy_event(m_connection.root()); + if (pipe(g_eventpipe) != 0) { + throw system_error("Failed to create event channel pipes"); } - m_log.info("Joining active threads"); - for (auto&& thread_ : m_threads) { - if (thread_.joinable()) { - thread_.join(); - } - } + m_log.trace("controller: Install signal handler"); + struct sigaction act {}; + memset(&act, 0, sizeof(act)); + act.sa_handler = &interrupt_handler; + sigaction(SIGINT, &act, nullptr); + sigaction(SIGQUIT, &act, nullptr); + sigaction(SIGTERM, &act, nullptr); + sigaction(SIGUSR1, &act, nullptr); + sigaction(SIGALRM, &act, nullptr); - m_log.info("Waiting for spawned processes"); - while (process_util::notify_childprocess()) { - ; - } - - m_connection.flush(); -} - -void controller::setup() { - if (!m_writeback) { - m_connection.ensure_event_mask(m_connection.root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY); - } - - string bs{m_conf.section()}; m_log.trace("controller: Setup user-defined modules"); + size_t created_modules{0}; for (int i = 0; i < 3; i++) { - alignment align = static_cast(i + 1); - string confkey; + alignment align{static_cast(i + 1)}; + string configured_modules; if (align == alignment::LEFT) { - confkey = "modules-left"; + configured_modules = m_conf.get(m_conf.section(), "modules-left", ""); } else if (align == alignment::CENTER) { - confkey = "modules-center"; + configured_modules = m_conf.get(m_conf.section(), "modules-center", ""); } else if (align == alignment::RIGHT) { - confkey = "modules-right"; + configured_modules = m_conf.get(m_conf.section(), "modules-right", ""); } - for (auto& module_name : string_util::split(m_conf.get(bs, confkey, ""), ' ')) { + for (auto& module_name : string_util::split(configured_modules, ' ')) { if (module_name.empty()) { continue; } try { auto type = m_conf.get("module/" + module_name, "type"); + if (type == "custom/ipc" && !m_ipc) { throw application_error("Inter-process messaging needs to be enabled"); } - unique_ptr module{make_module(move(type), m_bar->settings(), module_name)}; + auto module = make_module(move(type), m_bar->settings(), 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->set_update_cb([&] { enqueue(make_update_evt(false)); }); + module->set_stop_cb([&] { enqueue(make_check_evt()); }); module->setup(); - m_eventloop->add_module(align, move(module)); - } catch (const std::runtime_error& err) { + m_modules[align].emplace_back(move(module)); + + created_modules++; + } catch (const runtime_error& err) { m_log.err("Disabling module \"%s\" (reason: %s)", module_name, err.what()); } } } - if (!m_eventloop->module_count()) { + if (!created_modules) { throw application_error("No modules created"); } } -bool controller::run() { - assert(!m_connection.connection_has_error()); - - m_log.info("Starting application"); - m_running = true; - - m_sig.attach(this); - - if (m_confwatch && !m_writeback) { - m_threads.emplace_back(thread(&controller::wait_for_configwatch, this)); - } - if (m_ipc) { - m_threads.emplace_back(thread(&ipc::receive_messages, m_ipc.get())); - } - if (!m_writeback) { - m_threads.emplace_back(thread(&controller::wait_for_xevent, this)); - } - if (m_eventloop) { - m_threads.emplace_back(thread(&controller::wait_for_eventloop, this)); - } - - m_log.trace("controller: Wait for signal"); - m_waiting = true; - - sigemptyset(&m_waitmask); - sigaddset(&m_waitmask, SIGINT); - sigaddset(&m_waitmask, SIGQUIT); - sigaddset(&m_waitmask, SIGTERM); - sigaddset(&m_waitmask, SIGUSR1); - sigaddset(&m_waitmask, SIGALRM); - - int caught_signal = 0; - sigwait(&m_waitmask, &caught_signal); - - m_running = false; - m_waiting = false; - - if (caught_signal == SIGUSR1) { - m_reload = true; - } - - m_log.warn("Termination signal received, shutting down..."); - m_log.trace("controller: Caught signal %d", caught_signal); +/** + * Deconstruct controller + */ +controller::~controller() { + m_log.trace("controller: Uninstall sighandler"); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + m_log.trace("controller: Detach signal receiver"); m_sig.detach(this); - if (m_eventloop) { - // Signal the eventloop, in case it's still running - m_eventloop->enqueue(eventloop::make_quit_evt(false)); + m_log.trace("controller: Stop modules"); + for (auto&& block : m_modules) { + for (auto&& module : block.second) { + auto module_name = module->name(); + auto cleanup_ms = time_util::measure([&module] { + module->stop(); + module.reset(); + }); + m_log.info("Deconstruction of %s took %lu ms.", module_name, cleanup_ms); + } + } +} - m_log.trace("controller: Stopping event loop"); - m_eventloop->stop(); +/** + * Run the main loop + */ +bool controller::run(bool writeback) { + assert(!m_connection.connection_has_error()); + + m_writeback = writeback; + + m_log.info("Starting application"); + m_sig.attach(this); + + size_t started_modules{0}; + for (const auto& block : m_modules) { + for (const auto& module : block.second) { + try { + m_log.info("Starting %s", module->name()); + module->start(); + started_modules++; + } catch (const application_error& err) { + m_log.err("Failed to start '%s' (reason: %s)", module->name(), err.what()); + } + } + } + + if (!started_modules) { + throw application_error("No modules started"); + } + + m_connection.flush(); + + read_events(); + + m_log.warn("Termination signal received, shutting down..."); + + return !g_reload; +} + +/** + * Enqueue event + */ +bool controller::enqueue(event&& evt) { + if (!m_queue.enqueue(move(evt))) { + m_log.warn("Failed to enqueue event"); + return false; + } + write(g_eventpipe[PIPE_WRITE], " ", 1); + return true; +} + +/** + * Enqueue input data + */ +bool controller::enqueue(string&& input_data) { + if (!m_inputdata.empty()) { + m_log.trace("controller: Swallowing input event (pending data)"); + } else if (chrono::system_clock::now() - m_swallow_input < m_lastinput) { + m_log.trace("controller: Swallowing input event (throttled)"); + } else { + m_inputdata = move(input_data); + return enqueue(make_input_evt()); + } + return false; +} + +/** + * Read events from configured file descriptors + */ +void controller::read_events() { + int fd_confwatch{0}; + int fd_connection{0}; + int fd_event{0}; + int fd_ipc{0}; + + vector fds; + fds.emplace_back((fd_event = g_eventpipe[PIPE_READ])); + fds.emplace_back((fd_connection = m_connection.get_file_descriptor())); + + if (m_confwatch) { + m_log.trace("controller: Attach config watch"); + m_confwatch->attach(IN_MODIFY); + fds.emplace_back((fd_confwatch = m_confwatch->get_file_descriptor())); } if (m_ipc) { - m_ipc.reset(); + fds.emplace_back((fd_ipc = m_ipc->get_file_descriptor())); } - if (!m_writeback && m_confwatch) { - m_log.trace("controller: Removing config watch"); - m_confwatch->remove(true); - } + while (!g_terminate) { + fd_set readfds{}; + FD_ZERO(&readfds); - return !m_running && !m_reload; -} - -const bar_settings controller::opts() const { - return m_bar->settings(); -} - -void controller::wait_for_configwatch() { - try { - m_log.trace("controller: Attach config watch"); - m_confwatch->attach(IN_MODIFY); - - m_log.trace("controller: Wait for config file inotify event"); - if (m_confwatch->await_match() && m_running) { - m_log.info("Configuration file changed"); - kill(getpid(), SIGUSR1); + int maxfd{0}; + for (auto&& fd : fds) { + FD_SET(fd, &readfds); + maxfd = std::max(maxfd, fd); } - } catch (const system_error& err) { - m_log.err(err.what()); - m_log.trace("controller: Reset config watch"); - m_confwatch.reset(); - } -} -void controller::wait_for_xevent() { - m_log.trace("controller: Listen for X events"); - m_connection.flush(); + // Wait until event is ready on one of the configured streams + int events = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr); - while (m_running) { - try { - auto evt = m_connection.wait_for_event(); - if (evt && m_running) { - m_connection.dispatch_event(evt); - } - } catch (xpp::connection_error& err) { - m_log.err("X connection error, terminating... (what: %s)", m_connection.error_str(err.code())); - } catch (const exception& err) { - m_log.err("Error in X event loop: %s", err.what()); - } - if (m_connection.connection_has_error()) { + // Check for errors + if (events == -1 || g_terminate || m_connection.connection_has_error()) { break; } - } - if (m_running) { - kill(getpid(), SIGTERM); + // Process event on the internal fd + if (fd_event && FD_ISSET(fd_event, &readfds)) { + process_eventqueue(); + char buffer[BUFSIZ]{'\0'}; + read(fd_event, &buffer, BUFSIZ); + } + + // Process event on the config inotify watch fd + if (fd_confwatch && FD_ISSET(fd_confwatch, &readfds) && m_confwatch->await_match()) { + m_log.info("Configuration file changed"); + g_terminate = 1; + g_reload = 1; + } + + // Process event on the xcb connection fd + if (fd_connection && FD_ISSET(fd_connection, &readfds)) { + shared_ptr evt; + while ((evt = m_connection.poll_for_event())) { + try { + m_connection.dispatch_event(evt); + } catch (xpp::connection_error& err) { + m_log.err("X connection error, terminating... (what: %s)", m_connection.error_str(err.code())); + } catch (const exception& err) { + m_log.err("Error in X event loop: %s", err.what()); + } + } + } + + // Process event on the ipc fd + if (fd_ipc && FD_ISSET(fd_ipc, &readfds)) { + m_ipc->receive_message(); + fds.erase(std::remove_if(fds.begin(), fds.end(), [fd_ipc](int fd) { return fd == fd_ipc; })); + fds.emplace_back((fd_ipc = m_ipc->get_file_descriptor())); + } } } -void controller::wait_for_eventloop() { - m_eventloop->start(); +/** + * Dequeue items from the eventqueue + */ +void controller::process_eventqueue() { + event evt{}; - this_thread::sleep_for(std::chrono::milliseconds{250}); + if (!m_queue.try_dequeue(evt)) { + return m_log.err("Failed to dequeue event"); + } - if (m_running) { - m_log.trace("controller: eventloop ended, raising SIGALRM"); - kill(getpid(), SIGALRM); + if (evt.type == static_cast(event_type::INPUT)) { + process_inputdata(); + } else { + event next{}; + size_t swallowed{0}; + while (swallowed++ < m_swallow_limit && m_queue.wait_dequeue_timed(next, m_swallow_update)) { + if (next.type == static_cast(event_type::QUIT)) { + evt = next; + break; + } else if (next.type == static_cast(event_type::INPUT)) { + evt = next; + break; + } else if (evt.type != next.type) { + m_queue.try_enqueue(move(next)); + break; + } else { + m_log.trace_x("controller: Swallowing event within timeframe"); + evt = next; + } + } + + if (evt.type == static_cast(event_type::INPUT)) { + process_inputdata(); + } else if (evt.type == static_cast(event_type::QUIT)) { + m_sig.emit(sig_ev::process_quit{make_quit_evt(evt.flag)}); + } else if (evt.type == static_cast(event_type::UPDATE)) { + m_sig.emit(sig_ev::process_update{make_update_evt(evt.flag)}); + } else if (evt.type == static_cast(event_type::CHECK)) { + m_sig.emit(sig_ev::process_check{make_check_evt()}); + } else { + m_log.warn("Unknown event type for enqueued event (%d)", evt.type); + } } } +/** + * Process stored input data + */ +void controller::process_inputdata() { + if (!m_inputdata.empty()) { + m_sig.emit(sig_ev::process_input{move(m_inputdata)}); + m_lastinput = chrono::time_point_cast(chrono::system_clock::now()); + m_inputdata.clear(); + } +} + +/** + * Process eventqueue update event + */ bool controller::on(const sig_ev::process_update& evt) { bool force{evt.data()->flag}; @@ -276,7 +358,7 @@ bool controller::on(const sig_ev::process_update& evt) { string margin_left(bar.module_margin.left, ' '); string margin_right(bar.module_margin.right, ' '); - for (const auto& block : m_eventloop->modules()) { + for (const auto& block : m_modules) { string block_contents; bool is_left = false; bool is_center = false; @@ -349,9 +431,22 @@ bool controller::on(const sig_ev::process_update& evt) { return true; } +/** + * Process eventqueue input event + */ bool controller::on(const sig_ev::process_input& evt) { try { - string input{*evt.data()}; + string input{*evt()}; + + for (auto&& block : m_modules) { + for (auto&& module : block.second) { + if (module->receive_events() && module->handle_event(input)) { + return true; + } + } + } + + m_log.warn("Input event \"%s\" was rejected by all modules, passing to shell...", input); if (m_command) { m_log.warn("Terminating previous shell command"); @@ -370,16 +465,35 @@ bool controller::on(const sig_ev::process_input& evt) { return true; } -bool controller::on(const sig_ev::process_quit&) { - kill(getpid(), SIGUSR1); - return false; +/** + * Process eventqueue quit event + */ +bool controller::on(const sig_ev::process_quit& evt) { + bool reload{evt.data()->flag}; + raise(reload ? SIGUSR1 : SIGALRM); + return true; } -bool controller::on(const sig_ui::button_press& evt) { - if (!m_eventloop) { - return false; +/** + * Process eventqueue check event + */ +bool controller::on(const sig_ev::process_check&) { + for (const auto& block : m_modules) { + for (const auto& module : block.second) { + if (module->running()) { + return true; + } + } } + m_log.warn("No running modules..."); + enqueue(make_quit_evt(false)); + return true; +} +/** + * Process ui button press event + */ +bool controller::on(const sig_ui::button_press& evt) { string input{*evt.data()}; if (input.empty()) { @@ -387,9 +501,13 @@ bool controller::on(const sig_ui::button_press& evt) { return false; } - return m_sig.emit(enqueue_input{move(input)}); + enqueue(move(input)); + return true; } +/** + * Process ipc action messages + */ bool controller::on(const sig_ipc::process_action& evt) { ipc_action a{*evt.data()}; string action{a.payload}; @@ -401,9 +519,13 @@ bool controller::on(const sig_ipc::process_action& evt) { } m_log.info("Enqueuing ipc action: %s", action); - return m_sig.emit(enqueue_input{move(action)}); + enqueue(move(action)); + return true; } +/** + * Process ipc command messages + */ bool controller::on(const sig_ipc::process_command& evt) { ipc_command c{*evt.data()}; string command{c.payload}; @@ -414,9 +536,9 @@ bool controller::on(const sig_ipc::process_command& evt) { } if (command == "quit") { - m_eventloop->enqueue(eventloop::make_quit_evt(false)); + enqueue(make_quit_evt(false)); } else if (command == "restart") { - m_eventloop->enqueue(eventloop::make_quit_evt(true)); + enqueue(make_quit_evt(true)); } else { m_log.warn("\"%s\" is not a valid ipc command", command); } @@ -424,10 +546,13 @@ bool controller::on(const sig_ipc::process_command& evt) { return true; } +/** + * Process ipc hook messages + */ bool controller::on(const sig_ipc::process_hook& evt) { const ipc_hook hook{*evt.data()}; - for (const auto& block : m_eventloop->modules()) { + for (const auto& block : m_modules) { for (const auto& module : block.second) { auto ipc = dynamic_cast(module.get()); if (ipc != nullptr) { diff --git a/src/components/eventloop.cpp b/src/components/eventloop.cpp deleted file mode 100644 index 885dc13d..00000000 --- a/src/components/eventloop.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include -#include - -#include "components/config.hpp" -#include "components/eventloop.hpp" -#include "components/logger.hpp" -#include "components/types.hpp" -#include "events/signal.hpp" -#include "modules/meta/base.hpp" -#include "utils/factory.hpp" -#include "utils/string.hpp" -#include "utils/time.hpp" -#include "x11/color.hpp" - -POLYBAR_NS - -/** - * Create instance - */ -eventloop::make_type eventloop::make() { - return factory_util::unique(signal_emitter::make(), logger::make(), config::make()); -} - -/** - * Construct eventloop instance - */ -eventloop::eventloop(signal_emitter& emitter, const logger& logger, const config& config) - : m_sig(emitter), m_log(logger), m_conf(config) { - GET_CONFIG_VALUE("settings", m_swallow_input, "throttle-input-for"); - DEPR_CONFIG_VALUE("settings", m_swallow_limit, "eventloop-swallow", "throttle-output"); - DEPR_CONFIG_VALUE("settings", m_swallow_update, "eventloop-swallow-time", "throttle-output-for"); - m_sig.attach(this); -} - -/** - * Deconstruct eventloop - */ -eventloop::~eventloop() { - m_sig.detach(this); - - for (auto&& block : m_modules) { - for (auto&& module : block.second) { - auto module_name = module->name(); - auto cleanup_ms = time_util::measure([&module] { - module->stop(); - module.reset(); - }); - m_log.info("eventloop: Deconstruction of %s took %lu microsec.", module_name, cleanup_ms); - } - } -} - -/** - * Start module and worker threads - */ -void eventloop::start() { - m_log.info("Starting event loop"); - m_running = true; - dispatch_modules(); - - while (m_running) { - event evt{}, next{}; - - m_queue.wait_dequeue(evt); - - if (!m_running) { - break; - } - - if (evt.type == static_cast(event_type::INPUT)) { - handle_inputdata(); - } else { - size_t swallowed{0}; - while (swallowed++ < m_swallow_limit && m_queue.wait_dequeue_timed(next, m_swallow_update)) { - if (next.type == static_cast(event_type::QUIT)) { - evt = next; - break; - - } else if (next.type == static_cast(event_type::INPUT)) { - evt = next; - break; - - } else if (evt.type != next.type) { - m_queue.try_enqueue(move(next)); - break; - - } else { - m_log.trace_x("eventloop: Swallowing event within timeframe"); - evt = next; - } - } - - if (evt.type == static_cast(event_type::INPUT)) { - handle_inputdata(); - } else if (evt.type == static_cast(event_type::QUIT)) { - m_sig.emit(process_quit{make_input_evt()}); - } else if (evt.type == static_cast(event_type::UPDATE)) { - m_sig.emit(process_update{make_update_evt(evt.flag)}); - } else if (evt.type == static_cast(event_type::CHECK)) { - m_sig.emit(process_check{make_check_evt()}); - } else { - m_log.warn("Unknown event type for enqueued event (%d)", evt.type); - } - } - } - - m_log.info("Queue worker done"); -} - -/** - * Stop main loop by enqueuing a QUIT event - */ -void eventloop::stop() { - m_log.info("Stopping event loop"); - m_running = false; - m_sig.emit(enqueue_quit{make_quit_evt(false)}); -} - -/** - * Enqueue event - */ -bool eventloop::enqueue(event&& evt) { - if (!m_queue.enqueue(move(evt))) { - m_log.warn("Failed to enqueue event"); - return false; - } - return true; -} - -/** - * Enqueue input data - */ -bool eventloop::enqueue(string&& input_data) { - if (!m_inputlock.try_lock()) { - return false; - } - - std::lock_guard guard(m_inputlock, std::adopt_lock); - - if (!m_inputdata.empty()) { - m_log.trace("eventloop: Swallowing input event (pending data)"); - return false; - } else if (chrono::system_clock::now() - m_swallow_input < m_lastinput) { - m_log.trace("eventloop: Swallowing input event (throttled)"); - return false; - } - - m_inputdata = move(input_data); - - return enqueue(make_input_evt()); -} - -/** - * Add module to alignment block - */ -void eventloop::add_module(const alignment pos, module_t&& module) { - auto it = m_modules.lower_bound(pos); - - if (it != m_modules.end() && !(m_modules.key_comp()(pos, it->first))) { - it->second.emplace_back(forward(module)); - } else { - vector vec; - vec.emplace_back(forward(module)); - m_modules.insert(it, modulemap_t::value_type(pos, move(vec))); - } -} - -/** - * Get reference to module map - */ -const modulemap_t& eventloop::modules() const { - return m_modules; -} - -/** - * Get loaded module count - */ -size_t eventloop::module_count() const { - size_t count{0}; - for (auto&& block : m_modules) { - count += block.second.size(); - } - return count; -} - -/** - * Start module threads - */ -void eventloop::dispatch_modules() { - for (const auto& block : m_modules) { - for (const auto& module : block.second) { - try { - m_log.info("Starting %s", module->name()); - module->start(); - } catch (const application_error& err) { - m_log.err("Failed to start '%s' (reason: %s)", module->name(), err.what()); - } - } - } -} - -/** - * Process pending input data - */ -void eventloop::handle_inputdata() { - std::lock_guard guard(m_inputlock); - if (!m_inputdata.empty()) { - m_sig.emit(process_input{move(m_inputdata)}); - m_lastinput = chrono::time_point_cast(chrono::system_clock::now()); - m_inputdata.clear(); - } -} - -bool eventloop::on(const process_input& evt) { - string input{*evt()}; - - for (auto&& block : m_modules) { - for (auto&& module : block.second) { - if (module->receive_events() && module->handle_event(input)) { - return true; - } - } - } - - m_log.warn("Input event \"%s\" was rejected by all modules, passing to shell...", input); - - return false; -} - -bool eventloop::on(const process_check&) { - for (const auto& block : m_modules) { - for (const auto& module : block.second) { - if (m_running && module->running()) { - return true; - } - } - } - m_log.warn("No running modules..."); - enqueue(make_quit_evt(false)); - return true; -} - -bool eventloop::on(const process_quit& evt) { - const event quit{static_cast(*evt())}; - m_log.info("Processing QUIT event (reload=%i)", quit.flag); - m_running = false; - 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(*evt())); -} - -bool eventloop::on(const enqueue_quit& evt) { - if (m_running) { - const event quit{reinterpret_cast(*evt())}; - m_log.info("Enqueuing QUIT event (reload=%i)", quit.flag); - return enqueue(static_cast(*evt())); - } - return true; -} - -bool eventloop::on(const enqueue_update& evt) { - event update{reinterpret_cast(*evt())}; - 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(string{move(*evt())}); -} - -bool eventloop::on(const enqueue_check& evt) { - event check{reinterpret_cast(*evt())}; - m_log.trace("eventloop: enqueuing CHECK event"); - return enqueue(move(check)); -} - -eventloop::event eventloop::make_quit_evt(bool reload) { - return event{static_cast(event_type::QUIT), reload}; -} -eventloop::event eventloop::make_update_evt(bool force) { - return event{static_cast(event_type::UPDATE), force}; -} -eventloop::event eventloop::make_input_evt() { - return event{static_cast(event_type::INPUT)}; -} -eventloop::event eventloop::make_check_evt() { - return event{static_cast(event_type::CHECK)}; -} - -POLYBAR_NS_END diff --git a/src/components/ipc.cpp b/src/components/ipc.cpp index 7c576f0f..fe022def 100644 --- a/src/components/ipc.cpp +++ b/src/components/ipc.cpp @@ -23,50 +23,49 @@ ipc::make_type ipc::make() { } /** - * Interrupt the blocked listener and - * remove the file handler + * Construct ipc handler + */ +ipc::ipc(signal_emitter& emitter, const logger& logger) : m_sig(emitter), m_log(logger) { + m_path = string_util::replace(PATH_MESSAGING_FIFO, "%pid%", to_string(getpid())); + + if (mkfifo(m_path.c_str(), 0666) == -1) { + throw system_error("Failed to create ipc channel"); + } + + m_log.info("Created ipc channel at: %s", m_path); + m_fd = file_util::make_file_descriptor(m_path, O_RDONLY | O_NONBLOCK); +} + +/** + * Deconstruct ipc handler */ ipc::~ipc() { - m_running = false; - - if (!m_fifo.empty()) { - m_log.info("Interrupting ipc message receiver"); - - auto f = make_unique(m_fifo); - char p[1]{'q'}; - - fwrite(p, sizeof(char), sizeof(p), (*f)()); - unlink(m_fifo.c_str()); + if (!m_path.empty()) { + m_log.trace("ipc: Removing file handle"); + unlink(m_path.c_str()); } } /** - * Start listening for event messages + * Receive available ipc messages and delegate valid events */ -void ipc::receive_messages() { - m_running = true; - m_fifo = string_util::replace(PATH_MESSAGING_FIFO, "%pid%", to_string(getpid())); +void ipc::receive_message() { + m_log.info("Receiving ipc message"); - if (mkfifo(m_fifo.c_str(), 0666) == -1) { - m_log.err("Failed to create messaging channel"); + char buffer[BUFSIZ]{'\0'}; + ssize_t bytes_read{0}; + + if ((bytes_read = read(*m_fd.get(), &buffer, BUFSIZ)) == -1) { + m_log.err("Failed to read from ipc channel (err: %s)", strerror(errno)); } - m_log.info("Listening for ipc messages on: %s", m_fifo); - - while ((m_fd = open(m_fifo.c_str(), O_RDONLY)) != -1 && m_running) { - parse(io_util::readline(m_fd)); - close(m_fd); - } -} - -/** - * Process received message and delegate - * valid events to the target modules - */ -void ipc::parse(const string& payload) const { - if (payload.empty()) { + if (!bytes_read) { return; - } else if (payload.find(ipc_command::prefix) == 0) { + } + + string payload{string_util::trim(string{buffer}, '\n')}; + + if (payload.find(ipc_command::prefix) == 0) { ipc_command msg{}; memcpy(msg.payload, &payload[0], payload.size()); m_sig.emit(process_command{move(msg)}); @@ -78,9 +77,18 @@ void ipc::parse(const string& payload) const { ipc_action msg{}; memcpy(msg.payload, &payload[0], payload.size()); m_sig.emit(process_action{move(msg)}); - } else { + } else if (!payload.empty()) { m_log.warn("Received unknown ipc message: (payload=%s)", payload); } + + m_fd = file_util::make_file_descriptor(m_path, O_RDONLY | O_NONBLOCK); +} + +/** + * Get the file descriptor to the ipc channel + */ +int ipc::get_file_descriptor() const { + return *m_fd.get(); } POLYBAR_NS_END diff --git a/src/components/parser.cpp b/src/components/parser.cpp index 8a1adc06..fe5adede 100644 --- a/src/components/parser.cpp +++ b/src/components/parser.cpp @@ -12,20 +12,27 @@ POLYBAR_NS using namespace signals::parser; +/** + * Create instance + */ +parser::make_type parser::make() { + return factory_util::unique(signal_emitter::make()); +} + /** * Construct parser instance */ -parser::parser(signal_emitter& emitter, const bar_settings& bar) : m_sig(emitter), m_bar(bar) {} +parser::parser(signal_emitter& emitter) : m_sig(emitter) {} /** * Process input string */ -void parser::operator()(string data) { +void parser::parse(const bar_settings& bar, string data) { while (!data.empty()) { size_t pos{string::npos}; if (data.compare(0, 2, "%{") == 0 && (pos = data.find('}')) != string::npos) { - codeblock(data.substr(2, pos - 2)); + codeblock(data.substr(2, pos - 2), bar); data.erase(0, pos + 1); } else if ((pos = data.find("%{")) != string::npos) { data.erase(0, text(data.substr(0, pos))); @@ -42,7 +49,7 @@ void parser::operator()(string data) { /** * Process contents within tag blocks, i.e: %{...} */ -void parser::codeblock(string&& data) { +void parser::codeblock(string&& data, const bar_settings& bar) { size_t pos; while (data.length()) { @@ -66,11 +73,11 @@ void parser::codeblock(string&& data) { switch (tag) { case 'B': - m_sig.emit(change_background{parse_color(value, m_bar.background)}); + m_sig.emit(change_background{parse_color(value, bar.background)}); break; case 'F': - m_sig.emit(change_foreground{parse_color(value, m_bar.foreground)}); + m_sig.emit(change_foreground{parse_color(value, bar.foreground)}); break; case 'T': @@ -78,21 +85,21 @@ void parser::codeblock(string&& data) { break; case 'U': - m_sig.emit(change_underline{parse_color(value, m_bar.underline.color)}); - m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)}); + m_sig.emit(change_underline{parse_color(value, bar.underline.color)}); + m_sig.emit(change_overline{parse_color(value, bar.overline.color)}); break; case 'u': - m_sig.emit(change_underline{parse_color(value, m_bar.underline.color)}); + m_sig.emit(change_underline{parse_color(value, bar.underline.color)}); break; case 'o': - m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)}); + m_sig.emit(change_overline{parse_color(value, bar.overline.color)}); break; case 'R': - m_sig.emit(change_background{parse_color(value, m_bar.foreground)}); - m_sig.emit(change_foreground{parse_color(value, m_bar.background)}); + m_sig.emit(change_background{parse_color(value, bar.foreground)}); + m_sig.emit(change_foreground{parse_color(value, bar.background)}); break; case 'O': diff --git a/src/components/screen.cpp b/src/components/screen.cpp index e67bb3ca..3baa6e8a 100644 --- a/src/components/screen.cpp +++ b/src/components/screen.cpp @@ -2,7 +2,6 @@ #include #include "components/config.hpp" -#include "components/eventloop.hpp" #include "components/logger.hpp" #include "components/screen.hpp" #include "components/types.hpp" @@ -14,7 +13,7 @@ POLYBAR_NS -using namespace signals::eventloop; +using namespace signals::eventqueue; /** * Create instance @@ -107,7 +106,7 @@ 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_sig.emit(process_quit{make_quit_evt(true)}); m_sigraised = true; } diff --git a/src/main.cpp b/src/main.cpp index f5bc0ccf..f7004fde 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,25 @@ #include #include "common.hpp" +#include "components/bar.hpp" #include "components/command_line.hpp" #include "components/config.hpp" #include "components/controller.hpp" +#include "components/ipc.hpp" #include "components/logger.hpp" +#include "components/parser.hpp" +#include "components/renderer.hpp" #include "config.hpp" #include "utils/env.hpp" +#include "utils/file.hpp" #include "utils/inotify.hpp" #include "utils/process.hpp" +#include "x11/connection.hpp" +#include "x11/tray_manager.hpp" #include "x11/xutils.hpp" using namespace polybar; -struct exit_success {}; -struct exit_failure {}; - int main(int argc, char** argv) { // clang-format off const command_line::options opts{ @@ -41,27 +45,19 @@ int main(int argc, char** argv) { // Connect to X server //================================================== XInitThreads(); - shared_ptr xcbconn{xutils::get_connection()}; + + // Store the xcb connection pointer with a disconnect deleter + shared_ptr xcbconn{xutils::get_connection().get(), xutils::xcb_connection_deleter{}}; if (!xcbconn) { logger.err("A connection to X could not be established... "); - throw exit_failure{}; + return EXIT_FAILURE; } connection conn{xcbconn.get()}; conn.preload_atoms(); conn.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 //================================================== @@ -79,13 +75,13 @@ int main(int argc, char** argv) { if (cli->has("help")) { cli->usage(); - throw exit_success{}; + return EXIT_SUCCESS; } else if (cli->has("version")) { print_build_info(version_details(args)); - throw exit_success{}; + return EXIT_SUCCESS; } else if (args.empty() || args[0][0] == '-') { cli->usage(); - throw exit_failure{}; + return EXIT_FAILURE; } //================================================== @@ -110,62 +106,45 @@ int main(int argc, char** argv) { //================================================== if (cli->has("dump")) { std::cout << conf.get(conf.section(), cli->get("dump")) << std::endl; - throw exit_success{}; + return EXIT_SUCCESS; + } + if (cli->has("print-wmname")) { + std::cout << bar::make()->settings().wmname << std::endl; + return EXIT_SUCCESS; } //================================================== // Create controller and run application //================================================== - string path_confwatch; - bool enable_ipc{false}; + unique_ptr ipc{}; + unique_ptr config_watch{}; - if (!cli->has("print-wmname")) { - enable_ipc = conf.get(conf.section(), "enable-ipc", false); + if (conf.get(conf.section(), "enable-ipc", false)) { + ipc = ipc::make(); } - if (!cli->has("print-wmname") && cli->has("reload")) { - path_confwatch = conf.filepath(); + if (cli->has("reload")) { + config_watch = inotify_util::make_watch(conf.filepath()); } - unique_ptr ctrl{controller::make(move(path_confwatch), move(enable_ipc), cli->has("stdout"))}; + auto ctrl = controller::make(move(ipc), move(config_watch)); - if (cli->has("print-wmname")) { - std::cout << ctrl->opts().wmname << std::endl; - throw exit_success{}; - } - - ctrl->setup(); - - if (!ctrl->run()) { + if (!ctrl->run(cli->has("stdout"))) { 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) { - exit_code = EXIT_FAILURE; } catch (const exception& err) { logger.err(err.what()); exit_code = EXIT_FAILURE; } - if (!reload) { - logger.info("Reached end of application..."); - return exit_code; - } + logger.info("Waiting for spawned processes to end"); + while (process_util::notify_childprocess()) + ; - try { - logger.warn("Re-launching application..."); + if (reload) { logger.info("Re-launching application..."); process_util::exec(move(argv[0]), move(argv)); - } catch (const system_error& err) { - logger.err("execlp() failed (%s)", strerror(errno)); } - return EXIT_FAILURE; + logger.info("Reached end of application..."); + return exit_code; } diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index c08f75f5..8df4ff3a 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -257,8 +257,10 @@ namespace modules { * Get the current capacity level */ int battery_module::current_percentage() { - auto capacity_now = std::strtoul(file_util::get_contents(m_valuepath[battery_value::CAPACITY]).c_str(), nullptr, 10); - auto capacity_max = std::strtoul(file_util::get_contents(m_valuepath[battery_value::CAPACITY_MAX]).c_str(), nullptr, 10); + auto capacity_now = + std::strtoul(file_util::get_contents(m_valuepath[battery_value::CAPACITY]).c_str(), nullptr, 10); + auto capacity_max = + std::strtoul(file_util::get_contents(m_valuepath[battery_value::CAPACITY_MAX]).c_str(), nullptr, 10); auto percentage = math_util::percentage(capacity_now, 0UL, capacity_max); return percentage < m_fullat ? percentage : 100; diff --git a/src/modules/volume.cpp b/src/modules/volume.cpp index 02efb1bc..238634e6 100644 --- a/src/modules/volume.cpp +++ b/src/modules/volume.cpp @@ -85,6 +85,7 @@ namespace modules { void volume_module::teardown() { m_mixer.clear(); m_ctrl.clear(); + snd_config_update_free_global(); } bool volume_module::has_event() { diff --git a/src/modules/xkeyboard.cpp b/src/modules/xkeyboard.cpp index 924991aa..7c428cfd 100644 --- a/src/modules/xkeyboard.cpp +++ b/src/modules/xkeyboard.cpp @@ -100,9 +100,10 @@ namespace modules { */ bool xkeyboard_module::build(builder* builder, const string& tag) const { if (tag == TAG_LABEL_LAYOUT) { - builder->cmd(mousebtn::LEFT, EVENT_SWITCH); + bool precond{m_keyboard && m_keyboard->size() > 1}; + builder->cmd(mousebtn::LEFT, EVENT_SWITCH, precond); builder->node(m_layout); - builder->cmd_close(); + builder->cmd_close(precond); } else if (tag == TAG_LABEL_INDICATOR) { size_t n{0}; for (auto&& indicator : m_indicators) { @@ -139,6 +140,8 @@ namespace modules { m_keyboard->current(current_group); m_connection.flush(); + update(); + return true; } diff --git a/src/utils/command.cpp b/src/utils/command.cpp index ab051711..144a211a 100644 --- a/src/utils/command.cpp +++ b/src/utils/command.cpp @@ -10,196 +10,194 @@ POLYBAR_NS -namespace command_util { - command::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(cmd)) { - if (pipe(m_stdin) != 0) { - throw command_strerror("Failed to allocate input stream"); - } - if (pipe(m_stdout) != 0) { - throw command_strerror("Failed to allocate output stream"); - } +command::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(cmd)) { + if (pipe(m_stdin) != 0) { + throw command_error("Failed to allocate input stream"); } - - command::~command() { - if (is_running()) { - terminate(); - } - - if (m_stdin[PIPE_READ] > 0) { - close(m_stdin[PIPE_READ]); - } - if (m_stdin[PIPE_WRITE] > 0) { - close(m_stdin[PIPE_WRITE]); - } - if (m_stdout[PIPE_READ] > 0) { - close(m_stdout[PIPE_READ]); - } - if (m_stdout[PIPE_WRITE] > 0) { - close(m_stdout[PIPE_WRITE]); - } - } - - /** - * Execute the command - */ - int command::exec(bool wait_for_completion) { - if ((m_forkpid = fork()) == -1) { - throw system_error("Failed to fork process"); - } - - if (process_util::in_forked_process(m_forkpid)) { - if (dup2(m_stdin[PIPE_READ], STDIN_FILENO) == -1) { - throw command_strerror("Failed to redirect stdin in child process"); - } - if (dup2(m_stdout[PIPE_WRITE], STDOUT_FILENO) == -1) { - throw command_strerror("Failed to redirect stdout in child process"); - } - if (dup2(m_stdout[PIPE_WRITE], STDERR_FILENO) == -1) { - throw command_strerror("Failed to redirect stderr in child process"); - } - - // Close file descriptors that won't be used by the child - if ((m_stdin[PIPE_READ] = close(m_stdin[PIPE_READ])) == -1) { - throw command_strerror("Failed to close fd"); - } - if ((m_stdin[PIPE_WRITE] = close(m_stdin[PIPE_WRITE])) == -1) { - throw command_strerror("Failed to close fd"); - } - if ((m_stdout[PIPE_READ] = close(m_stdout[PIPE_READ])) == -1) { - throw command_strerror("Failed to close fd"); - } - if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1) { - throw command_strerror("Failed to close fd"); - } - - // Make sure SIGTERM is raised - process_util::unblock_signal(SIGTERM); - - setpgid(m_forkpid, 0); - process_util::exec_sh(m_cmd.c_str()); - } else { - // Close file descriptors that won't be used by the parent - if ((m_stdin[PIPE_READ] = close(m_stdin[PIPE_READ])) == -1) { - throw command_strerror("Failed to close fd"); - } - if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1) { - throw command_strerror("Failed to close fd"); - } - - if (wait_for_completion) { - auto status = wait(); - m_forkpid = -1; - return status; - } - } - - return EXIT_SUCCESS; - } - - void command::terminate() { - try { - if (is_running()) { - m_log.trace("command: Sending SIGTERM to running child process (%d)", m_forkpid); - killpg(m_forkpid, SIGTERM); - wait(); - } - } catch (const command_error& err) { - m_log.warn("%s", err.what()); - } - - m_forkpid = -1; - } - - /** - * Check if command is running - */ - bool command::is_running() { - if (m_forkpid > 0) { - return process_util::wait_for_completion_nohang(m_forkpid, &m_forkstatus) > -1; - } - return false; - } - - /** - * Wait for the child processs to finish - */ - int command::wait() { - do { - m_log.trace("command: Waiting for pid %d to finish...", m_forkpid); - - process_util::wait_for_completion(m_forkpid, &m_forkstatus, WCONTINUED | WUNTRACED); - - if (WIFEXITED(m_forkstatus) && m_forkstatus > 0) { - m_log.warn("command: Exited with failed status %d", WEXITSTATUS(m_forkstatus)); - } else if (WIFEXITED(m_forkstatus)) { - m_log.trace("command: Exited with status %d", WEXITSTATUS(m_forkstatus)); - } else if (WIFSIGNALED(m_forkstatus)) { - m_log.trace("command: killed by signal %d", WTERMSIG(m_forkstatus)); - } else if (WIFSTOPPED(m_forkstatus)) { - m_log.trace("command: Stopped by signal %d", WSTOPSIG(m_forkstatus)); - } else if (WIFCONTINUED(m_forkstatus)) { - m_log.trace("command: Continued"); - } else { - break; - } - } while (!WIFEXITED(m_forkstatus) && !WIFSIGNALED(m_forkstatus)); - - return m_forkstatus; - } - - /** - * Tail command output - * - * @note: This is a blocking call and will not - * end until the stream is closed - */ - void command::tail(callback cb) { - io_util::tail(m_stdout[PIPE_READ], move(cb)); - } - - /** - * Write line to command input channel - */ - int command::writeline(string data) { - std::lock_guard lck(m_pipelock); - return io_util::writeline(m_stdin[PIPE_WRITE], move(data)); - } - - /** - * Read a line from the commands output stream - */ - string command::readline() { - std::lock_guard lck(m_pipelock); - return io_util::readline(m_stdout[PIPE_READ]); - } - - /** - * Get command output channel - */ - int command::get_stdout(int c) { - return m_stdout[c]; - } - - /** - * Get command input channel - */ - int command::get_stdin(int c) { - return m_stdin[c]; - } - - /** - * Get command pid - */ - pid_t command::get_pid() { - return m_forkpid; - } - - /** - * Get command exit status - */ - int command::get_exit_status() { - return m_forkstatus; + if (pipe(m_stdout) != 0) { + throw command_error("Failed to allocate output stream"); } } +command::~command() { + if (is_running()) { + terminate(); + } + + if (m_stdin[PIPE_READ] > 0) { + close(m_stdin[PIPE_READ]); + } + if (m_stdin[PIPE_WRITE] > 0) { + close(m_stdin[PIPE_WRITE]); + } + if (m_stdout[PIPE_READ] > 0) { + close(m_stdout[PIPE_READ]); + } + if (m_stdout[PIPE_WRITE] > 0) { + close(m_stdout[PIPE_WRITE]); + } +} + +/** + * Execute the command + */ +int command::exec(bool wait_for_completion) { + if ((m_forkpid = fork()) == -1) { + throw system_error("Failed to fork process"); + } + + if (process_util::in_forked_process(m_forkpid)) { + if (dup2(m_stdin[PIPE_READ], STDIN_FILENO) == -1) { + throw command_error("Failed to redirect stdin in child process"); + } + if (dup2(m_stdout[PIPE_WRITE], STDOUT_FILENO) == -1) { + throw command_error("Failed to redirect stdout in child process"); + } + if (dup2(m_stdout[PIPE_WRITE], STDERR_FILENO) == -1) { + throw command_error("Failed to redirect stderr in child process"); + } + + // Close file descriptors that won't be used by the child + if ((m_stdin[PIPE_READ] = close(m_stdin[PIPE_READ])) == -1) { + throw command_error("Failed to close fd"); + } + if ((m_stdin[PIPE_WRITE] = close(m_stdin[PIPE_WRITE])) == -1) { + throw command_error("Failed to close fd"); + } + if ((m_stdout[PIPE_READ] = close(m_stdout[PIPE_READ])) == -1) { + throw command_error("Failed to close fd"); + } + if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1) { + throw command_error("Failed to close fd"); + } + + // Make sure SIGTERM is raised + process_util::unblock_signal(SIGTERM); + + setpgid(m_forkpid, 0); + process_util::exec_sh(m_cmd.c_str()); + } else { + // Close file descriptors that won't be used by the parent + if ((m_stdin[PIPE_READ] = close(m_stdin[PIPE_READ])) == -1) { + throw command_error("Failed to close fd"); + } + if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1) { + throw command_error("Failed to close fd"); + } + + if (wait_for_completion) { + auto status = wait(); + m_forkpid = -1; + return status; + } + } + + return EXIT_SUCCESS; +} + +void command::terminate() { + try { + if (is_running()) { + m_log.trace("command: Sending SIGTERM to running child process (%d)", m_forkpid); + killpg(m_forkpid, SIGTERM); + wait(); + } + } catch (const command_error& err) { + m_log.warn("%s", err.what()); + } + + m_forkpid = -1; +} + +/** + * Check if command is running + */ +bool command::is_running() { + if (m_forkpid > 0) { + return process_util::wait_for_completion_nohang(m_forkpid, &m_forkstatus) > -1; + } + return false; +} + +/** + * Wait for the child processs to finish + */ +int command::wait() { + do { + m_log.trace("command: Waiting for pid %d to finish...", m_forkpid); + + process_util::wait_for_completion(m_forkpid, &m_forkstatus, WCONTINUED | WUNTRACED); + + if (WIFEXITED(m_forkstatus) && m_forkstatus > 0) { + m_log.warn("command: Exited with failed status %d", WEXITSTATUS(m_forkstatus)); + } else if (WIFEXITED(m_forkstatus)) { + m_log.trace("command: Exited with status %d", WEXITSTATUS(m_forkstatus)); + } else if (WIFSIGNALED(m_forkstatus)) { + m_log.trace("command: killed by signal %d", WTERMSIG(m_forkstatus)); + } else if (WIFSTOPPED(m_forkstatus)) { + m_log.trace("command: Stopped by signal %d", WSTOPSIG(m_forkstatus)); + } else if (WIFCONTINUED(m_forkstatus)) { + m_log.trace("command: Continued"); + } else { + break; + } + } while (!WIFEXITED(m_forkstatus) && !WIFSIGNALED(m_forkstatus)); + + return m_forkstatus; +} + +/** + * Tail command output + * + * @note: This is a blocking call and will not + * end until the stream is closed + */ +void command::tail(callback cb) { + io_util::tail(m_stdout[PIPE_READ], move(cb)); +} + +/** + * Write line to command input channel + */ +int command::writeline(string data) { + std::lock_guard lck(m_pipelock); + return io_util::writeline(m_stdin[PIPE_WRITE], move(data)); +} + +/** + * Read a line from the commands output stream + */ +string command::readline() { + std::lock_guard lck(m_pipelock); + return io_util::readline(m_stdout[PIPE_READ]); +} + +/** + * Get command output channel + */ +int command::get_stdout(int c) { + return m_stdout[c]; +} + +/** + * Get command input channel + */ +int command::get_stdin(int c) { + return m_stdin[c]; +} + +/** + * Get command pid + */ +pid_t command::get_pid() { + return m_forkpid; +} + +/** + * Get command exit status + */ +int command::get_exit_status() { + return m_forkstatus; +} + POLYBAR_NS_END diff --git a/src/utils/factory.cpp b/src/utils/factory.cpp new file mode 100644 index 00000000..1ae28359 --- /dev/null +++ b/src/utils/factory.cpp @@ -0,0 +1,8 @@ +#include "utils/factory.hpp" + +POLYBAR_NS + +factory_util::detail::null_deleter factory_util::null_deleter{}; +factory_util::detail::fd_deleter factory_util::fd_deleter{}; + +POLYBAR_NS_END diff --git a/src/utils/file.cpp b/src/utils/file.cpp index 8a7ad002..17307763 100644 --- a/src/utils/file.cpp +++ b/src/utils/file.cpp @@ -10,30 +10,67 @@ POLYBAR_NS +/** + * Deconstruct file wrapper + */ +file_ptr::file_ptr(const string& path, const string& mode) : m_path(string(path)), m_mode(string(mode)) { + m_ptr = fopen(m_path.c_str(), m_mode.c_str()); +} + +/** + * Deconstruct file wrapper + */ +file_ptr::~file_ptr() { + if (m_ptr != nullptr) { + fclose(m_ptr); + } +} + +/** + * Logical operator testing if the file handler was created + */ +file_ptr::operator bool() { + return m_ptr != nullptr; +} + +/** + * Call operator returning a pointer to the file handler + */ +FILE* file_ptr::operator()() { + return m_ptr; +} + +/** + * Construct file descriptor wrapper + */ +file_descriptor::file_descriptor(const string& path, int flags) { + if ((m_fd = open(path.c_str(), flags)) == -1) { + throw system_error("Failed to open file descriptor"); + } +} + +/** + * Construct file descriptor wrapper from an existing handle + */ +file_descriptor::file_descriptor(int fd) : m_fd(fd) {} + +/** + * Deconstruct file descriptor wrapper + */ +file_descriptor::~file_descriptor() { + if (m_fd > 0) { + close(m_fd); + } +} + +/** + * Conversion operator returning the fd handle + */ +file_descriptor::operator int() { + return m_fd; +} + namespace file_util { - /** - * Destructor: close file handler - */ - file_ptr::~file_ptr() { - if (m_ptr != nullptr) { - fclose(m_ptr); - } - } - - /** - * Logical operator testing if the file handler was created - */ - file_ptr::operator bool() { - return m_ptr != nullptr; - } - - /** - * Call operator returning a pointer to the file handler - */ - FILE* file_ptr::operator()() { - return m_ptr; - } - /** * Checks if the given file exist */ diff --git a/src/utils/inotify.cpp b/src/utils/inotify.cpp index 53ad3fca..53cb6143 100644 --- a/src/utils/inotify.cpp +++ b/src/utils/inotify.cpp @@ -1,123 +1,123 @@ #include #include "errors.hpp" -#include "utils/factory.hpp" #include "utils/inotify.hpp" #include "utils/memory.hpp" POLYBAR_NS -namespace inotify_util { - /** - * Construct instance - */ - inotify_watch::inotify_watch(string path) : m_path(move(path)) {} +/** + * Construct inotify watch + */ +inotify_watch::inotify_watch(string path) : m_path(move(path)) {} - /** - * Destructor - */ - inotify_watch::~inotify_watch() noexcept { - if (m_wd != -1) { - inotify_rm_watch(m_fd, m_wd); - } - if (m_fd != -1) { - close(m_fd); - } +/** + * Deconstruct inotify watch + */ +inotify_watch::~inotify_watch() { + if (m_wd != -1) { + inotify_rm_watch(m_fd, m_wd); } - - /** - * Attach inotify watch - */ - void inotify_watch::attach(int mask) { - if (m_fd == -1 && (m_fd = inotify_init()) == -1) { - throw system_error("Failed to allocate inotify fd"); - } - if ((m_wd = inotify_add_watch(m_fd, m_path.c_str(), mask)) == -1) { - throw system_error("Failed to attach inotify watch"); - } - m_mask |= mask; - } - - /** - * Remove inotify watch - */ - void inotify_watch::remove(bool force) { - if (inotify_rm_watch(m_fd, m_wd) == -1 && !force) { - throw system_error("Failed to remove inotify watch"); - } - m_wd = -1; - m_mask = 0; - } - - /** - * Poll the inotify fd for events - * - * @brief A wait_ms of -1 blocks until an event is fired - */ - bool inotify_watch::poll(int wait_ms) { - if (m_fd == -1) { - return false; - } - - struct pollfd fds[1]; - fds[0].fd = m_fd; - fds[0].events = POLLIN; - - ::poll(fds, 1, wait_ms); - - return fds[0].revents & POLLIN; - } - - /** - * Get the latest inotify event - */ - unique_ptr inotify_watch::get_event() { - auto event = factory_util::unique(); - - if (m_fd == -1 || m_wd == -1) { - return event; - } - - char buffer[1024]; - size_t bytes = read(m_fd, buffer, 1024); - size_t len = 0; - - while (len < bytes) { - auto* e = reinterpret_cast<::inotify_event*>(&buffer[len]); - - event->filename = e->len ? e->name : m_path; - event->wd = e->wd; - event->cookie = e->cookie; - event->is_dir = e->mask & IN_ISDIR; - event->mask |= e->mask; - - len += sizeof(event_t) + e->len; - } - - return event; - } - - /** - * Wait for matching event - */ - bool inotify_watch::await_match() { - return (get_event()->mask & m_mask) == m_mask; - } - - /** - * Get watch file path - */ - const string inotify_watch::path() const { - return m_path; - } - - bool match(const event_t* evt, int mask) { - return (evt->mask & mask) == mask; - } - - watch_t make_watch(string path) { - return make_unique(move(path)); + if (m_fd != -1) { + close(m_fd); } } +/** + * Attach inotify watch + */ +void inotify_watch::attach(int mask) { + if (m_fd == -1 && (m_fd = inotify_init()) == -1) { + throw system_error("Failed to allocate inotify fd"); + } + if ((m_wd = inotify_add_watch(m_fd, m_path.c_str(), mask)) == -1) { + throw system_error("Failed to attach inotify watch"); + } + m_mask |= mask; +} + +/** + * Remove inotify watch + */ +void inotify_watch::remove(bool force) { + if (inotify_rm_watch(m_fd, m_wd) == -1 && !force) { + throw system_error("Failed to remove inotify watch"); + } + m_wd = -1; + m_mask = 0; +} + +/** + * Poll the inotify fd for events + * + * @brief A wait_ms of -1 blocks until an event is fired + */ +bool inotify_watch::poll(int wait_ms) { + if (m_fd == -1) { + return false; + } + + struct pollfd fds[1]; + fds[0].fd = m_fd; + fds[0].events = POLLIN; + + ::poll(fds, 1, wait_ms); + + return fds[0].revents & POLLIN; +} + +/** + * Get the latest inotify event + */ +unique_ptr inotify_watch::get_event() { + auto event = factory_util::unique(); + + if (m_fd == -1 || m_wd == -1) { + return event; + } + + char buffer[1024]; + size_t bytes = read(m_fd, buffer, 1024); + size_t len = 0; + + while (len < bytes) { + auto* e = reinterpret_cast<::inotify_event*>(&buffer[len]); + + event->filename = e->len ? e->name : m_path; + event->wd = e->wd; + event->cookie = e->cookie; + event->is_dir = e->mask & IN_ISDIR; + event->mask |= e->mask; + + len += sizeof(*e) + e->len; + } + + return event; +} + +/** + * Wait for matching event + */ +bool inotify_watch::await_match() { + return (get_event()->mask & m_mask) == m_mask; +} + +/** + * Get watch file path + */ +const string inotify_watch::path() const { + return m_path; +} + +/** + * Get the file descriptor associated with the watch + */ +int inotify_watch::get_file_descriptor() const { + return m_fd; +} + +bool match(const inotify_event* evt, int mask) { + return (evt->mask & mask) == mask; +} + POLYBAR_NS_END diff --git a/src/x11/connection.cpp b/src/x11/connection.cpp index c04ba88f..d188c2f9 100644 --- a/src/x11/connection.cpp +++ b/src/x11/connection.cpp @@ -160,21 +160,6 @@ void connection::send_client_message(const shared_ptr #include +#include #include "components/types.hpp" #include "utils/string.hpp" diff --git a/src/x11/tray_manager.cpp b/src/x11/tray_manager.cpp index 6080c197..a13eb7e7 100644 --- a/src/x11/tray_manager.cpp +++ b/src/x11/tray_manager.cpp @@ -3,7 +3,6 @@ #include #include "components/config.hpp" -#include "components/eventloop.hpp" #include "components/types.hpp" #include "errors.hpp" #include "events/signal.hpp" @@ -284,7 +283,7 @@ void tray_manager::deactivate(bool clear_selection) { m_connection.flush(); - m_sig.emit(process_update{eventloop::make_update_evt(true)}); + m_sig.emit(process_update{make_update_evt(true)}); } /** @@ -323,7 +322,7 @@ void tray_manager::reconfigure() { m_connection.flush(); - m_sig.emit(process_update{eventloop::make_update_evt(true)}); + m_sig.emit(process_update{make_update_evt(true)}); } /** @@ -875,7 +874,7 @@ bool tray_manager::is_embedded(const xcb_window_t& win) const { shared_ptr tray_manager::find_client(const xcb_window_t& win) const { for (auto&& client : m_clients) { if (client->match(win)) { - return shared_ptr{client.get(), factory_util::null_deleter{}}; + return shared_ptr{client.get(), factory_util::null_deleter}; } } return nullptr; diff --git a/src/x11/wm.cpp b/src/x11/wm.cpp index 7946847f..430752b7 100644 --- a/src/x11/wm.cpp +++ b/src/x11/wm.cpp @@ -38,7 +38,8 @@ namespace wm_util { } void set_wm_window_opacity(xcb_connection_t* conn, xcb_window_t win, uint64_t values) { - xcb_intern_atom_reply_t* reply{xcb_intern_atom_reply(conn, xcb_intern_atom(conn, false, 22, "_NET_WM_WINDOW_OPACITY"), nullptr)}; + xcb_intern_atom_reply_t* reply{ + xcb_intern_atom_reply(conn, xcb_intern_atom(conn, false, 22, "_NET_WM_WINDOW_OPACITY"), nullptr)}; if (reply) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, reply->atom, XCB_ATOM_CARDINAL, 32, 1, &values); xcb_flush(conn); diff --git a/src/x11/xlib.cpp b/src/x11/xlib.cpp index fd072d3d..7bde195a 100644 --- a/src/x11/xlib.cpp +++ b/src/x11/xlib.cpp @@ -9,7 +9,7 @@ namespace xlib { shared_ptr get_display() { if (!g_display_ptr) { - g_display_ptr = shared_ptr(XOpenDisplay(nullptr), [=](Display* ptr) { XCloseDisplay(ptr); }); + g_display_ptr = shared_ptr(XOpenDisplay(nullptr), factory_util::null_deleter); } return g_display_ptr; } diff --git a/src/x11/xutils.cpp b/src/x11/xutils.cpp index 824db97a..660f4e58 100644 --- a/src/x11/xutils.cpp +++ b/src/x11/xutils.cpp @@ -19,8 +19,7 @@ namespace xutils { if (dsp) { XSetEventQueueOwner(dsp.get(), XCBOwnsEventQueue); - g_connection_ptr = - shared_ptr(XGetXCBConnection(dsp.get()), [=](xcb_connection_t* c) { xcb_disconnect(c); }); + g_connection_ptr = shared_ptr(XGetXCBConnection(dsp.get()), factory_util::null_deleter); } } @@ -30,7 +29,7 @@ namespace xutils { int get_connection_fd() { if (!g_connection_fd) { auto fd = xcb_get_file_descriptor(get_connection().get()); - g_connection_fd = shared_ptr(new int{fd}, factory_util::fd_deleter{}); + g_connection_fd = shared_ptr(new int{fd}, factory_util::fd_deleter); } return *g_connection_fd.get();