refactor: Move all event handling to the controller

This commit is contained in:
Michael Carlberg 2016-12-20 05:05:43 +01:00
parent 8ed4de1dda
commit a89c4ef2dd
47 changed files with 1146 additions and 1436 deletions

View File

@ -29,8 +29,6 @@ namespace alsa {
void process_events(); void process_events();
private: private:
std::mutex m_lock{};
int m_numid{0}; int m_numid{0};
snd_ctl_t* m_ctl{nullptr}; snd_ctl_t* m_ctl{nullptr};

View File

@ -37,8 +37,6 @@ namespace alsa {
bool is_muted(); bool is_muted();
private: private:
std::mutex m_lock{};
snd_mixer_t* m_mixer{nullptr}; snd_mixer_t* m_mixer{nullptr};
snd_mixer_elem_t* m_elem{nullptr}; snd_mixer_elem_t* m_elem{nullptr};

View File

@ -1,41 +1,41 @@
#pragma once #pragma once
#include <mutex>
#include "common.hpp" #include "common.hpp"
#include "components/renderer.hpp"
#include "components/screen.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp" #include "events/signal_fwd.hpp"
#include "utils/concurrency.hpp"
#include "utils/throttle.hpp"
#include "x11/connection.hpp"
#include "x11/events.hpp" #include "x11/events.hpp"
#include "x11/tray_manager.hpp"
#include "x11/types.hpp" #include "x11/types.hpp"
#include "x11/window.hpp" #include "x11/window.hpp"
POLYBAR_NS POLYBAR_NS
// fwd // fwd
class screen; class connection;
class tray_manager;
class logger; class logger;
class parser;
class renderer;
class screen;
class signal_emitter;
class tray_manager;
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify, evt::leave_notify, evt::destroy_notify, evt::client_message> { class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify,
evt::leave_notify, evt::destroy_notify, evt::client_message> {
public: public:
using make_type = unique_ptr<bar>; using make_type = unique_ptr<bar>;
static make_type make(); static make_type make();
explicit bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, explicit bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger,
unique_ptr<screen> screen, unique_ptr<tray_manager> tray_manager); unique_ptr<screen>&& screen, unique_ptr<tray_manager>&& tray_manager, unique_ptr<parser>&& parser);
virtual ~bar();
~bar(); void parse(string&& data) const;
void parse(const string& data, bool force = false);
const bar_settings settings() const; const bar_settings settings() const;
void parse(const string& data, bool force = false);
protected: protected:
void restack_window(); void restack_window();
void reconfigure_pos(); void reconfigure_pos();
@ -57,10 +57,11 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
const config& m_conf; const config& m_conf;
const logger& m_log; const logger& m_log;
unique_ptr<screen> m_screen; unique_ptr<screen> m_screen;
unique_ptr<tray_manager> m_tray; unique_ptr<tray_manager> m_tray{};
unique_ptr<renderer> m_renderer; unique_ptr<renderer> m_renderer{};
unique_ptr<parser> m_parser{};
bar_settings m_opts; bar_settings m_opts{};
string m_lastinput{}; string m_lastinput{};

View File

@ -13,7 +13,6 @@ POLYBAR_NS
#define GET_CONFIG_VALUE(section, var, name) var = m_conf.get<decltype(var)>(section, name, var) #define GET_CONFIG_VALUE(section, var, name) var = m_conf.get<decltype(var)>(section, name, var)
#define REQ_CONFIG_VALUE(section, var, name) var = m_conf.get<decltype(var)>(section, name) #define REQ_CONFIG_VALUE(section, var, name) var = m_conf.get<decltype(var)>(section, name)
#define DEPR_CONFIG_VALUE(section, var, old, name) var = m_conf.deprecated<decltype(var)>(section, old, name, var)
DEFINE_ERROR(value_error); DEFINE_ERROR(value_error);
DEFINE_ERROR(key_error); DEFINE_ERROR(key_error);

View File

@ -1,70 +1,69 @@
#pragma once #pragma once
#include <csignal> #include <moodycamel/blockingconcurrentqueue.h>
#include "common.hpp" #include "common.hpp"
#include "components/bar.hpp"
#include "components/ipc.hpp"
#include "components/types.hpp"
#include "config.hpp" #include "config.hpp"
#include "events/signal_emitter.hpp"
#include "events/signal_fwd.hpp" #include "events/signal_fwd.hpp"
#include "events/signal_receiver.hpp" #include "events/signal_receiver.hpp"
#include "events/types.hpp"
#include "x11/types.hpp" #include "x11/types.hpp"
POLYBAR_NS POLYBAR_NS
// fwd decl {{{ // fwd decl {{{
enum class alignment : uint8_t;
class bar; class bar;
class command;
class config; class config;
class connection; class connection;
class eventloop; class inotify_watch;
class ipc;
class logger; class logger;
struct bar_settings; class signal_emitter;
namespace inotify_util {
class inotify_watch; namespace modules {
using watch_t = unique_ptr<inotify_watch>; struct module_interface;
} }
namespace command_util {
class command; using module_t = unique_ptr<modules::module_interface>;
} using modulemap_t = std::map<alignment, vector<module_t>>;
using command = command_util::command;
using command_t = unique_ptr<command>;
// }}} // }}}
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_ui = signals::ui;
namespace sig_ipc = signals::ipc; namespace sig_ipc = signals::ipc;
class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, sig_ev::process_update, sig_ev::process_input, class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, sig_ev::process_update, sig_ev::process_input,
sig_ev::process_quit, sig_ui::button_press, sig_ipc::process_action, sig_ipc::process_command, sig_ev::process_quit, sig_ev::process_check, sig_ipc::process_action, sig_ipc::process_command,
sig_ipc::process_hook> { sig_ipc::process_hook, sig_ui::button_press> {
public: public:
using make_type = unique_ptr<controller>; using make_type = unique_ptr<controller>;
static make_type make(string&& path_confwatch, bool enable_ipc = false, bool writeback = false); static make_type make(unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& config_watch);
explicit controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config, explicit controller(connection&, signal_emitter&, const logger&, const config&, unique_ptr<bar>&&, unique_ptr<ipc>&&,
unique_ptr<eventloop>&& eventloop, unique_ptr<bar>&& bar, unique_ptr<ipc>&& ipc, watch_t&& confwatch, bool writeback); unique_ptr<inotify_watch>&&);
~controller(); ~controller();
void setup(); bool run(bool writeback = false);
bool run();
const bar_settings opts() const; bool enqueue(event&& evt);
bool enqueue(string&& input_data);
protected: protected:
void wait_for_signal(); void read_events();
void wait_for_xevent(); void process_eventqueue();
void wait_for_eventloop(); void process_inputdata();
void wait_for_configwatch();
bool on(const sig_ev::process_update& evt); bool on(const sig_ev::process_update& evt);
bool on(const sig_ev::process_input& evt); bool on(const sig_ev::process_input& evt);
bool on(const sig_ev::process_quit& 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_ui::button_press& evt);
bool on(const sig_ipc::process_action& evt); bool on(const sig_ipc::process_action& evt);
bool on(const sig_ipc::process_command& evt); bool on(const sig_ipc::process_command& evt);
@ -75,21 +74,51 @@ class controller : public signal_receiver<SIGN_PRIORITY_CONTROLLER, sig_ev::proc
signal_emitter& m_sig; signal_emitter& m_sig;
const logger& m_log; const logger& m_log;
const config& m_conf; const config& m_conf;
unique_ptr<eventloop> m_eventloop;
unique_ptr<bar> m_bar; unique_ptr<bar> m_bar;
unique_ptr<ipc> m_ipc; unique_ptr<ipc> m_ipc;
unique_ptr<inotify_watch> m_confwatch;
unique_ptr<command> m_command;
stateflag m_running{false}; /**
stateflag m_reload{false}; * @brief Controls weather the output gets printed to stdout
stateflag m_waiting{false}; */
sigset_t m_waitmask{};
vector<thread> m_threads;
watch_t m_confwatch;
command_t m_command;
bool m_writeback{false}; bool m_writeback{false};
/**
* @brief Internal event queue
*/
using queue_t = moodycamel::BlockingConcurrentQueue<event>;
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<chrono::system_clock, chrono::milliseconds> m_lastinput;
/**
* @brief Input data
*/
string m_inputdata;
}; };
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -1,141 +0,0 @@
#pragma once
#include <moodycamel/blockingconcurrentqueue.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#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<modules::module_interface>;
using modulemap_t = std::map<alignment, vector<module_t>>;
namespace chrono = std::chrono;
using namespace std::chrono_literals;
class eventloop : public signal_receiver<SIGN_PRIORITY_EVENTLOOP, process_quit, process_input, process_check,
enqueue_event, enqueue_quit, enqueue_update, enqueue_input, enqueue_check> {
public:
enum class event_type : uint8_t {
NONE = 0,
UPDATE,
CHECK,
INPUT,
QUIT,
};
struct event {
uint8_t type{static_cast<uint8_t>(event_type::NONE)};
bool flag{false};
};
template <typename EventType>
using queue_t = moodycamel::BlockingConcurrentQueue<EventType>;
public:
using make_type = unique_ptr<eventloop>;
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<event> 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<chrono::system_clock, chrono::milliseconds> m_lastinput;
/**
* @brief Mutex used to guard input data
*/
std::mutex m_inputlock{};
/**
* @brief Input data
*/
string m_inputdata;
};
POLYBAR_NS_END

View File

@ -5,6 +5,7 @@
#include "events/signal_emitter.hpp" #include "events/signal_emitter.hpp"
#include "utils/concurrency.hpp" #include "utils/concurrency.hpp"
#include "utils/functional.hpp" #include "utils/functional.hpp"
#include "utils/file.hpp"
POLYBAR_NS POLYBAR_NS
@ -36,20 +37,18 @@ class ipc {
using make_type = unique_ptr<ipc>; using make_type = unique_ptr<ipc>;
static make_type make(); 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(); ~ipc();
void receive_messages(); void receive_message();
int get_file_descriptor() const;
protected:
void parse(const string& payload) const;
private: private:
signal_emitter& m_sig; signal_emitter& m_sig;
const logger& m_log; const logger& m_log;
string m_fifo{};
int m_fd{0}; string m_path{};
stateflag m_running{false}; shared_ptr<file_descriptor> m_fd{};
}; };
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -6,9 +6,9 @@
POLYBAR_NS POLYBAR_NS
class signal_emitter; class signal_emitter;
struct bar_settings;
enum class attribute : uint8_t; enum class attribute : uint8_t;
enum class mousebtn : uint8_t; enum class mousebtn : uint8_t;
struct bar_settings;
DEFINE_ERROR(parser_error); DEFINE_ERROR(parser_error);
DEFINE_CHILD_ERROR(unrecognized_token, parser_error); DEFINE_CHILD_ERROR(unrecognized_token, parser_error);
@ -21,12 +21,15 @@ class parser {
uint16_t data[128]{0U}; uint16_t data[128]{0U};
size_t length{0}; size_t length{0};
}; };
using make_type = unique_ptr<parser>;
static make_type make();
explicit parser(signal_emitter& emitter, const bar_settings& bar); public:
void operator()(string data); explicit parser(signal_emitter& emitter);
void parse(const bar_settings& bar, string data);
protected: protected:
void codeblock(string&& data); void codeblock(string&& data, const bar_settings& bar);
size_t text(string&& data); size_t text(string&& data);
uint32_t parse_color(const string& s, uint32_t fallback = 0); uint32_t parse_color(const string& s, uint32_t fallback = 0);
@ -37,8 +40,8 @@ class parser {
private: private:
signal_emitter& m_sig; signal_emitter& m_sig;
const bar_settings& m_bar;
vector<int> m_actions; vector<int> m_actions;
unique_ptr<parser> m_parser;
}; };
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -32,12 +32,11 @@
static const size_t EVENT_SIZE{64U}; static const size_t EVENT_SIZE{64U};
static const int SIGN_PRIORITY_EVENTLOOP{1}; static const int SIGN_PRIORITY_CONTROLLER{1};
static const int SIGN_PRIORITY_CONTROLLER{2}; static const int SIGN_PRIORITY_SCREEN{2};
static const int SIGN_PRIORITY_SCREEN{3}; static const int SIGN_PRIORITY_BAR{3};
static const int SIGN_PRIORITY_BAR{4}; static const int SIGN_PRIORITY_RENDERER{4};
static const int SIGN_PRIORITY_RENDERER{5}; static const int SIGN_PRIORITY_TRAY{5};
static const int SIGN_PRIORITY_TRAY{6};
static const int SINK_PRIORITY_BAR{1}; static const int SINK_PRIORITY_BAR{1};
static const int SINK_PRIORITY_SCREEN{2}; static const int SINK_PRIORITY_SCREEN{2};

View File

@ -14,6 +14,7 @@ using std::runtime_error;
class application_error : public runtime_error { class application_error : public runtime_error {
public: public:
explicit application_error(const string& message, int code = 0) : runtime_error(message), code(code) {} explicit application_error(const string& message, int code = 0) : runtime_error(message), code(code) {}
virtual ~application_error() {}
int code{0}; int code{0};
}; };
@ -22,6 +23,7 @@ class system_error : public application_error {
explicit system_error() : application_error(strerror(errno), errno) {} explicit system_error() : application_error(strerror(errno), errno) {}
explicit system_error(const string& message) explicit system_error(const string& message)
: application_error(message + " (reason: " + strerror(errno) + ")", errno) {} : application_error(message + " (reason: " + strerror(errno) + ")", errno) {}
virtual ~system_error() {}
}; };
#define DEFINE_CHILD_ERROR(error, parent) \ #define DEFINE_CHILD_ERROR(error, parent) \

View File

@ -2,10 +2,10 @@
#include "common.hpp" #include "common.hpp"
#include "components/eventloop.hpp"
#include "components/ipc.hpp" #include "components/ipc.hpp"
#include "components/parser.hpp" #include "components/parser.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "events/types.hpp"
#include "utils/functional.hpp" #include "utils/functional.hpp"
POLYBAR_NS POLYBAR_NS
@ -91,19 +91,11 @@ namespace signals {
// }}} // }}}
} }
namespace eventloop { namespace eventqueue {
using eventloop_t = polybar::eventloop; DEFINE_VALUE_SIGNAL(1, process_quit, event);
DEFINE_VALUE_SIGNAL(2, process_update, event);
DEFINE_VALUE_SIGNAL(1, process_quit, eventloop_t::event);
DEFINE_VALUE_SIGNAL(2, process_update, eventloop_t::event);
DEFINE_VALUE_SIGNAL(3, process_input, string); DEFINE_VALUE_SIGNAL(3, process_input, string);
DEFINE_VALUE_SIGNAL(4, process_check, eventloop_t::event); DEFINE_VALUE_SIGNAL(4, process_check, 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);
} }
namespace ipc { namespace ipc {

View File

@ -5,17 +5,11 @@
POLYBAR_NS POLYBAR_NS
namespace signals { namespace signals {
namespace eventloop { namespace eventqueue {
struct process_quit; struct process_quit;
struct process_update; struct process_update;
struct process_input; struct process_input;
struct process_check; struct process_check;
struct enqueue_event;
struct enqueue_quit;
struct enqueue_update;
struct enqueue_input;
struct enqueue_check;
} }
namespace ipc { namespace ipc {
struct process_command; struct process_command;

46
include/events/types.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <cstdint>
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<uint8_t>(event_type::QUIT), reload};
}
/**
* Create UPDATE event
*/
inline event make_update_evt(bool force = false) {
return event{static_cast<uint8_t>(event_type::UPDATE), force};
}
/**
* Create INPUT event
*/
inline event make_input_evt() {
return event{static_cast<uint8_t>(event_type::INPUT)};
}
/**
* Create CHECK event
*/
inline event make_check_evt() {
return event{static_cast<uint8_t>(event_type::CHECK)};
}
}

View File

@ -43,7 +43,7 @@ namespace modules {
template <class Impl> template <class Impl>
void inotify_module<Impl>::poll_events() { void inotify_module<Impl>::poll_events() {
vector<inotify_util::watch_t> watches; vector<unique_ptr<inotify_watch>> watches;
try { try {
for (auto&& w : m_watchlist) { for (auto&& w : m_watchlist) {

View File

@ -1,11 +1,9 @@
#pragma once #pragma once
#include <chrono> #include <chrono>
#include <csignal>
#include "adapters/mpd.hpp" #include "adapters/mpd.hpp"
#include "modules/meta/event_module.hpp" #include "modules/meta/event_module.hpp"
#include "utils/concurrency.hpp"
POLYBAR_NS POLYBAR_NS

View File

@ -1,87 +1,84 @@
#pragma once #pragma once
#include <mutex>
#include "common.hpp" #include "common.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "utils/concurrency.hpp"
#include "utils/factory.hpp" #include "utils/factory.hpp"
#include "utils/functional.hpp" #include "utils/functional.hpp"
POLYBAR_NS 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<string> 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 { 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<string> 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 <typename... Args> template <typename... Args>
unique_ptr<command> make_command(Args&&... args) { unique_ptr<command> make_command(Args&&... args) {
return factory_util::unique<command>(logger::make(), forward<Args>(args)...); return factory_util::unique<command>(logger::make(), forward<Args>(args)...);
} }
} }
using command = command_util::command;
using command_t = unique_ptr<command>;
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -7,18 +7,23 @@
POLYBAR_NS POLYBAR_NS
namespace factory_util { namespace factory_util {
struct null_deleter { namespace detail {
template <typename T> struct null_deleter {
void operator()(T*) const {} template <typename T>
}; void operator()(T*) const {}
};
struct fd_deleter { struct fd_deleter {
void operator()(int* fd) const { void operator()(int* fd) const {
if (fd != nullptr && *fd > 0) { if (fd != nullptr && *fd > 0) {
close(*fd); close(*fd);
}
} }
} };
}; }
extern detail::null_deleter null_deleter;
extern detail::fd_deleter fd_deleter;
template <typename T, typename... Deps> template <typename T, typename... Deps>
unique_ptr<T> unique(Deps&&... deps) { unique_ptr<T> unique(Deps&&... deps) {

View File

@ -1,39 +1,50 @@
#pragma once #pragma once
#include "common.hpp" #include "common.hpp"
#include "utils/factory.hpp"
POLYBAR_NS 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 { 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); bool exists(const string& filename);
string get_contents(const string& filename); string get_contents(const string& filename);
void set_block(int fd); void set_block(int fd);
void set_nonblock(int fd); void set_nonblock(int fd);
bool is_fifo(string filename); bool is_fifo(string filename);
template <typename... Args>
decltype(auto) make_file_descriptor(Args&&... args) {
return factory_util::shared<file_descriptor>(forward<Args>(args)...);
}
} }
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -5,6 +5,7 @@
#include <cstdio> #include <cstdio>
#include "common.hpp" #include "common.hpp"
#include "utils/factory.hpp"
POLYBAR_NS POLYBAR_NS
@ -16,33 +17,33 @@ struct inotify_event {
int mask = 0; 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<inotify_event> 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 { namespace inotify_util {
using event_t = inotify_event; bool match(const inotify_event* evt, int mask);
class inotify_watch { template <typename... Args>
public: decltype(auto) make_watch(Args&&... args) {
explicit inotify_watch(string path); return factory_util::unique<inotify_watch>(forward<Args>(args)...);
~inotify_watch() noexcept; }
void attach(int mask = IN_MODIFY);
void remove(bool force = false);
bool poll(int wait_ms = 1000);
unique_ptr<event_t> 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<inotify_watch>;
watch_t make_watch(string path);
bool match(const event_t* evt, int mask);
} }
POLYBAR_NS_END POLYBAR_NS_END

View File

@ -11,12 +11,12 @@ namespace chrono = std::chrono;
namespace time_util { namespace time_util {
using clock_t = chrono::high_resolution_clock; using clock_t = chrono::high_resolution_clock;
template <typename T, typename Dur = chrono::microseconds> template <typename Duration = chrono::milliseconds, typename T>
auto measure(const T& expr) noexcept { auto measure(const T& expr) noexcept {
auto start = clock_t::now(); auto start = clock_t::now();
expr(); expr();
auto finish = clock_t::now(); auto finish = clock_t::now();
return chrono::duration_cast<Dur>(finish - start).count(); return chrono::duration_cast<Duration>(finish - start).count();
} }
} }

View File

@ -71,8 +71,6 @@ class connection : public xpp_connection {
void send_client_message(const shared_ptr<xcb_client_message_event_t>& message, xcb_window_t target, void send_client_message(const shared_ptr<xcb_client_message_event_t>& message, xcb_window_t target,
uint32_t event_mask = 0xFFFFFF, bool propagate = false) const; 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); xcb_visualtype_t* visual_type(xcb_screen_t* screen, int match_depth = 32);
static string error_str(int error_code); static string error_str(int error_code);

View File

@ -29,7 +29,7 @@ POLYBAR_NS
namespace chrono = std::chrono; namespace chrono = std::chrono;
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace signals::eventloop; using namespace signals::eventqueue;
using namespace signals::ui; using namespace signals::ui;
// fwd declarations // fwd declarations

View File

@ -13,7 +13,7 @@ namespace wm_util {
void set_wm_state(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> states); void set_wm_state(xcb_connection_t* conn, xcb_window_t win, vector<xcb_atom_t> states);
void set_wm_pid(xcb_connection_t* conn, xcb_window_t win, pid_t pid); void set_wm_pid(xcb_connection_t* conn, xcb_window_t win, pid_t pid);
void set_wm_desktop(xcb_connection_t* conn, xcb_window_t win, uint32_t desktop = -1u); 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 POLYBAR_NS_END

View File

@ -13,6 +13,12 @@ class connection;
class config; class config;
namespace xutils { namespace xutils {
struct xcb_connection_deleter {
void operator()(xcb_connection_t* c) {
xcb_disconnect(c);
}
};
shared_ptr<xcb_connection_t> get_connection(); shared_ptr<xcb_connection_t> get_connection();
int get_connection_fd(); int get_connection_fd();

View File

@ -2,46 +2,8 @@
# Create executable # Create executable
# #
# Generate source tree {{{
file(GLOB_RECURSE SOURCES RELATIVE ${PROJECT_SOURCE_DIR}/src *.c[p]*) 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 {{{ # Locate dependencies {{{
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
@ -65,26 +27,38 @@ set(XCB_PROTOS xproto)
if(WITH_XRANDR) if(WITH_XRANDR)
set(XCB_PROTOS "${XCB_PROTOS}" randr) set(XCB_PROTOS "${XCB_PROTOS}" randr)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::randr::extension) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::randr::extension)
else()
list(REMOVE_ITEM SOURCES x11/randr.cpp)
endif() endif()
if(WITH_XRENDER) if(WITH_XRENDER)
set(XCB_PROTOS "${XCB_PROTOS}" render) set(XCB_PROTOS "${XCB_PROTOS}" render)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::render::extension) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::render::extension)
else()
list(REMOVE_ITEM SOURCES x11/render.cpp)
endif() endif()
if(WITH_XDAMAGE) if(WITH_XDAMAGE)
set(XCB_PROTOS "${XCB_PROTOS}" damage) set(XCB_PROTOS "${XCB_PROTOS}" damage)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::damage::extension) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::damage::extension)
else()
list(REMOVE_ITEM SOURCES x11/damage.cpp)
endif() endif()
if(WITH_XSYNC) if(WITH_XSYNC)
set(XCB_PROTOS "${XCB_PROTOS}" sync) set(XCB_PROTOS "${XCB_PROTOS}" sync)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::sync::extension) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::sync::extension)
else()
list(REMOVE_ITEM SOURCES x11/sync.cpp)
endif() endif()
if(WITH_XCOMPOSITE) if(WITH_XCOMPOSITE)
set(XCB_PROTOS "${XCB_PROTOS}" composite) set(XCB_PROTOS "${XCB_PROTOS}" composite)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::composite::extension) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::composite::extension)
else()
list(REMOVE_ITEM SOURCES x11/composite.cpp)
endif() endif()
if(WITH_XKB) if(WITH_XKB)
set(XCB_PROTOS "${XCB_PROTOS}" xkb) set(XCB_PROTOS "${XCB_PROTOS}" xkb)
set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::xkb::extension) set(XPP_EXTENSION_LIST ${XPP_EXTENSION_LIST} xpp::xkb::extension)
else()
list(REMOVE_ITEM SOURCES x11/xkb.cpp modules/xkeyboard.cpp)
endif() endif()
string(REPLACE ";" ", " XPP_EXTENSION_LIST "${XPP_EXTENSION_LIST}") string(REPLACE ";" ", " XPP_EXTENSION_LIST "${XPP_EXTENSION_LIST}")
@ -101,6 +75,8 @@ if(ENABLE_ALSA)
find_package(ALSA REQUIRED) find_package(ALSA REQUIRED)
set(APP_LIBRARIES ${APP_LIBRARIES} ${ALSA_LIBRARY}) set(APP_LIBRARIES ${APP_LIBRARIES} ${ALSA_LIBRARY})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${ALSA_INCLUDE_DIR}) 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() endif()
# }}} # }}}
@ -110,6 +86,8 @@ if(ENABLE_MPD)
find_package(LibMPDClient REQUIRED) find_package(LibMPDClient REQUIRED)
set(APP_LIBRARIES ${APP_LIBRARIES} ${LIBMPDCLIENT_LIBRARY}) set(APP_LIBRARIES ${APP_LIBRARIES} ${LIBMPDCLIENT_LIBRARY})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${LIBMPDCLIENT_INCLUDE_DIR}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${LIBMPDCLIENT_INCLUDE_DIR})
else()
list(REMOVE_ITEM SOURCES adapters/mpd.cpp modules/mpd.cpp)
endif() endif()
# }}} # }}}
@ -119,6 +97,8 @@ if(ENABLE_NETWORK)
find_package(Libiw REQUIRED) find_package(Libiw REQUIRED)
set(APP_LIBRARIES ${APP_LIBRARIES} ${LIBIW_LIBRARY}) set(APP_LIBRARIES ${APP_LIBRARIES} ${LIBIW_LIBRARY})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${LIBIW_INCLUDE_DIR}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${LIBIW_INCLUDE_DIR})
else()
list(REMOVE_ITEM SOURCES adapters/net.cpp modules/network.cpp)
endif() endif()
# }}} # }}}
@ -128,15 +108,19 @@ if(ENABLE_I3)
add_subdirectory(${PROJECT_SOURCE_DIR}/lib/i3ipcpp ${PROJECT_BINARY_DIR}/lib/i3ipcpp) add_subdirectory(${PROJECT_SOURCE_DIR}/lib/i3ipcpp ${PROJECT_BINARY_DIR}/lib/i3ipcpp)
set(APP_LIBRARIES ${APP_LIBRARIES} ${I3IPCPP_LIBRARIES}) set(APP_LIBRARIES ${APP_LIBRARIES} ${I3IPCPP_LIBRARIES})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${I3IPCPP_INCLUDE_DIRS}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${I3IPCPP_INCLUDE_DIRS})
else()
list(REMOVE_ITEM SOURCES modules/i3.cpp utils/i3.cpp)
endif() endif()
# }}} # }}}
# Optional dependency: i3ipcpp {{{ # Optional dependency: libcurl {{{
if(ENABLE_CURL) if(ENABLE_CURL)
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
set(APP_LIBRARIES ${APP_LIBRARIES} ${CURL_LIBRARY}) set(APP_LIBRARIES ${APP_LIBRARIES} ${CURL_LIBRARY})
set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CURL_INCLUDE_DIR}) set(APP_INCLUDE_DIRS ${APP_INCLUDE_DIRS} ${CURL_INCLUDE_DIR})
else()
list(REMOVE_ITEM SOURCES utils/http.cpp modules/github.cpp)
endif() endif()
# }}} # }}}

View File

@ -54,13 +54,9 @@ namespace alsa {
* Deconstruct control object * Deconstruct control object
*/ */
control::~control() { control::~control() {
std::lock_guard<std::mutex> guard(m_lock);
if (m_hctl != nullptr) { if (m_hctl != nullptr) {
snd_hctl_close(m_hctl); snd_hctl_close(m_hctl);
} }
snd_config_update_free_global();
} }
/** /**
@ -76,12 +72,6 @@ namespace alsa {
bool control::wait(int timeout) { bool control::wait(int timeout) {
assert(m_ctl); assert(m_ctl);
if (!m_lock.try_lock()) {
return false;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
int err{0}; int err{0};
if ((err = snd_ctl_wait(m_ctl, timeout)) == -1) { if ((err = snd_ctl_wait(m_ctl, timeout)) == -1) {
@ -108,12 +98,6 @@ namespace alsa {
bool control::test_device_plugged() { bool control::test_device_plugged() {
assert(m_elem); assert(m_elem);
if (!m_lock.try_lock()) {
return false;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
snd_ctl_elem_value_t* m_value{nullptr}; snd_ctl_elem_value_t* m_value{nullptr};
snd_ctl_elem_value_alloca(&m_value); snd_ctl_elem_value_alloca(&m_value);

View File

@ -45,8 +45,6 @@ namespace alsa {
* Deconstruct mixer * Deconstruct mixer
*/ */
mixer::~mixer() { mixer::~mixer() {
std::lock_guard<std::mutex> guard(m_lock);
if (m_mixer != nullptr) { if (m_mixer != nullptr) {
snd_mixer_close(m_mixer); snd_mixer_close(m_mixer);
} }
@ -65,20 +63,12 @@ namespace alsa {
bool mixer::wait(int timeout) { bool mixer::wait(int timeout) {
assert(m_mixer); assert(m_mixer);
if (!m_lock.try_lock()) {
return false;
}
std::unique_lock<std::mutex> guard(m_lock, std::adopt_lock);
int err = 0; int err = 0;
if ((err = snd_mixer_wait(m_mixer, timeout)) == -1) { if ((err = snd_mixer_wait(m_mixer, timeout)) == -1) {
throw_exception<mixer_error>("Failed to wait for events", err); throw_exception<mixer_error>("Failed to wait for events", err);
} }
guard.unlock();
return process_events() > 0; return process_events() > 0;
} }
@ -86,12 +76,6 @@ namespace alsa {
* Process queued mixer events * Process queued mixer events
*/ */
int mixer::process_events() { int mixer::process_events() {
if (!m_lock.try_lock()) {
return false;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
int num_events{0}; int num_events{0};
if ((num_events = snd_mixer_handle_events(m_mixer)) == -1) { if ((num_events = snd_mixer_handle_events(m_mixer)) == -1) {
throw_exception<mixer_error>("Failed to process pending events", num_events); throw_exception<mixer_error>("Failed to process pending events", num_events);
@ -106,12 +90,6 @@ namespace alsa {
int mixer::get_volume() { int mixer::get_volume() {
assert(m_elem != nullptr); assert(m_elem != nullptr);
if (!m_lock.try_lock()) {
return 0;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
long chan_n = 0, vol_total = 0, vol, vol_min, vol_max; 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); snd_mixer_selem_get_playback_volume_range(m_elem, &vol_min, &vol_max);
@ -133,12 +111,6 @@ namespace alsa {
int mixer::get_normalized_volume() { int mixer::get_normalized_volume() {
assert(m_elem != nullptr); assert(m_elem != nullptr);
if (!m_lock.try_lock()) {
return 0;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
long chan_n = 0, vol_total = 0, vol, vol_min, vol_max; long chan_n = 0, vol_total = 0, vol, vol_min, vol_max;
double normalized, min_norm; double normalized, min_norm;
@ -175,12 +147,6 @@ namespace alsa {
return; return;
} }
if (!m_lock.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
long vol_min, vol_max; long vol_min, vol_max;
snd_mixer_selem_get_playback_volume_range(m_elem, &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<int>(percentage, vol_min, vol_max)); snd_mixer_selem_set_playback_volume_all(m_elem, math_util::percentage_to_value<int>(percentage, vol_min, vol_max));
@ -196,12 +162,6 @@ namespace alsa {
return; return;
} }
if (!m_lock.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
long vol_min, vol_max; long vol_min, vol_max;
double min_norm; double min_norm;
percentage = percentage / 100.0f; percentage = percentage / 100.0f;
@ -227,12 +187,6 @@ namespace alsa {
void mixer::set_mute(bool mode) { void mixer::set_mute(bool mode) {
assert(m_elem != nullptr); assert(m_elem != nullptr);
if (!m_lock.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
snd_mixer_selem_set_playback_switch_all(m_elem, mode); snd_mixer_selem_set_playback_switch_all(m_elem, mode);
} }
@ -242,12 +196,6 @@ namespace alsa {
void mixer::toggle_mute() { void mixer::toggle_mute() {
assert(m_elem != nullptr); assert(m_elem != nullptr);
if (!m_lock.try_lock()) {
return;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
int state; int state;
snd_mixer_selem_get_playback_switch(m_elem, SND_MIXER_SCHN_MONO, &state); snd_mixer_selem_get_playback_switch(m_elem, SND_MIXER_SCHN_MONO, &state);
@ -260,12 +208,6 @@ namespace alsa {
bool mixer::is_muted() { bool mixer::is_muted() {
assert(m_elem != nullptr); assert(m_elem != nullptr);
if (!m_lock.try_lock()) {
return false;
}
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
int state = 0; int state = 0;
for (int i = 0; i <= SND_MIXER_SCHN_LAST; i++) { for (int i = 0; i <= SND_MIXER_SCHN_LAST; i++) {

View File

@ -5,6 +5,7 @@
#include "components/bar.hpp" #include "components/bar.hpp"
#include "components/config.hpp" #include "components/config.hpp"
#include "components/parser.hpp" #include "components/parser.hpp"
#include "components/renderer.hpp"
#include "components/screen.hpp" #include "components/screen.hpp"
#include "events/signal.hpp" #include "events/signal.hpp"
#include "events/signal_emitter.hpp" #include "events/signal_emitter.hpp"
@ -40,7 +41,8 @@ bar::make_type bar::make() {
config::make(), config::make(),
logger::make(), logger::make(),
screen::make(), screen::make(),
tray_manager::make()); tray_manager::make(),
parser::make());
// clang-format on // clang-format on
} }
@ -50,13 +52,14 @@ bar::make_type bar::make() {
* TODO: Break out all tray handling * TODO: Break out all tray handling
*/ */
bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger, bar::bar(connection& conn, signal_emitter& emitter, const config& config, const logger& logger,
unique_ptr<screen> screen, unique_ptr<tray_manager> tray_manager) unique_ptr<screen>&& screen, unique_ptr<tray_manager>&& tray_manager, unique_ptr<parser>&& parser)
: m_connection(conn) : m_connection(conn)
, m_sig(emitter) , m_sig(emitter)
, m_conf(config) , m_conf(config)
, m_log(logger) , m_log(logger)
, m_screen(move(screen)) , m_screen(forward<decltype(screen)>(screen))
, m_tray(move(tray_manager)) { , m_tray(forward<decltype(tray_manager)>(tray_manager))
, m_parser(forward<decltype(parser)>(parser)) {
string bs{m_conf.section()}; string bs{m_conf.section()};
// Get available RandR outputs // 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"); 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()) { if (monitor_name.empty()) {
monitor_name = monitors[0]->name; monitor_name = monitors[0]->name;
m_log.warn("No monitor specified, using \"%s\"", monitor_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); m_opts.monitor->x, m_opts.monitor->y);
try { try {
@ -316,10 +327,7 @@ void bar::parse(const string& data, bool force) {
m_renderer->fill_background(); m_renderer->fill_background();
try { try {
if (!data.empty()) { m_parser->parse(settings(), data);
parser parser{m_sig, m_opts};
parser(data);
}
} catch (const parser_error& err) { } catch (const parser_error& err) {
m_log.err("Failed to parse contents (reason: %s)", err.what()); m_log.err("Failed to parse contents (reason: %s)", err.what());
} }

View File

@ -1,266 +1,348 @@
#include "components/controller.hpp" #include <csignal>
#include "common.hpp"
#include "components/bar.hpp" #include "components/bar.hpp"
#include "components/config.hpp" #include "components/config.hpp"
#include "components/eventloop.hpp" #include "components/controller.hpp"
#include "components/ipc.hpp" #include "components/ipc.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "components/renderer.hpp"
#include "components/types.hpp"
#include "events/signal.hpp" #include "events/signal.hpp"
#include "events/signal_emitter.hpp"
#include "modules/meta/factory.hpp" #include "modules/meta/factory.hpp"
#include "utils/command.hpp" #include "utils/command.hpp"
#include "utils/factory.hpp" #include "utils/factory.hpp"
#include "utils/inotify.hpp" #include "utils/inotify.hpp"
#include "utils/process.hpp" #include "utils/process.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
#include "utils/time.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/tray_manager.hpp"
#include "x11/xutils.hpp" #include "x11/xutils.hpp"
POLYBAR_NS POLYBAR_NS
using namespace modules; int g_eventpipe[2]{0, 0};
sig_atomic_t g_reload{0};
sig_atomic_t g_terminate{0};
/** void interrupt_handler(int signum) {
* Create instance g_terminate = 1;
*/ g_reload = (signum == SIGUSR1);
controller::make_type controller::make(string&& path_confwatch, bool enable_ipc, bool writeback) { write(g_eventpipe[PIPE_WRITE], &g_terminate, 1);
// clang-format off
return factory_util::unique<controller>(
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<decltype(path_confwatch)>(move(path_confwatch))) : watch_t{},
writeback);
// clang-format on
} }
/** /**
* Construct controller object * Build controller instance
*/
controller::make_type controller::make(unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& config_watch) {
return factory_util::unique<controller>(connection::make(), signal_emitter::make(), logger::make(), config::make(),
bar::make(), forward<decltype(ipc)>(ipc), forward<decltype(config_watch)>(config_watch));
}
/**
* Construct controller
*/ */
controller::controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config, controller::controller(connection& conn, signal_emitter& emitter, const logger& logger, const config& config,
unique_ptr<eventloop>&& eventloop, unique_ptr<bar>&& bar, unique_ptr<ipc>&& ipc, watch_t&& confwatch, unique_ptr<bar>&& bar, unique_ptr<ipc>&& ipc, unique_ptr<inotify_watch>&& confwatch)
bool writeback)
: m_connection(conn) : m_connection(conn)
, m_sig(emitter) , m_sig(emitter)
, m_log(logger) , m_log(logger)
, m_conf(config) , m_conf(config)
, m_eventloop(forward<decltype(eventloop)>(eventloop))
, m_bar(forward<decltype(bar)>(bar)) , m_bar(forward<decltype(bar)>(bar))
, m_ipc(forward<decltype(ipc)>(ipc)) , m_ipc(forward<decltype(ipc)>(ipc))
, m_confwatch(forward<decltype(confwatch)>(confwatch)) , m_confwatch(forward<decltype(confwatch)>(confwatch)) {
, m_writeback(writeback) {} 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);
/** if (pipe(g_eventpipe) != 0) {
* Deconstruct controller object throw system_error("Failed to create event channel pipes");
*/
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());
} }
m_log.info("Joining active threads"); m_log.trace("controller: Install signal handler");
for (auto&& thread_ : m_threads) { struct sigaction act {};
if (thread_.joinable()) { memset(&act, 0, sizeof(act));
thread_.join(); 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"); m_log.trace("controller: Setup user-defined modules");
size_t created_modules{0};
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
alignment align = static_cast<alignment>(i + 1); alignment align{static_cast<alignment>(i + 1)};
string confkey; string configured_modules;
if (align == alignment::LEFT) { if (align == alignment::LEFT) {
confkey = "modules-left"; configured_modules = m_conf.get<string>(m_conf.section(), "modules-left", "");
} else if (align == alignment::CENTER) { } else if (align == alignment::CENTER) {
confkey = "modules-center"; configured_modules = m_conf.get<string>(m_conf.section(), "modules-center", "");
} else if (align == alignment::RIGHT) { } else if (align == alignment::RIGHT) {
confkey = "modules-right"; configured_modules = m_conf.get<string>(m_conf.section(), "modules-right", "");
} }
for (auto& module_name : string_util::split(m_conf.get<string>(bs, confkey, ""), ' ')) { for (auto& module_name : string_util::split(configured_modules, ' ')) {
if (module_name.empty()) { if (module_name.empty()) {
continue; continue;
} }
try { try {
auto type = m_conf.get<string>("module/" + module_name, "type"); auto type = m_conf.get<string>("module/" + module_name, "type");
if (type == "custom/ipc" && !m_ipc) { if (type == "custom/ipc" && !m_ipc) {
throw application_error("Inter-process messaging needs to be enabled"); throw application_error("Inter-process messaging needs to be enabled");
} }
unique_ptr<module_interface> 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([&] { module->set_update_cb([&] { enqueue(make_update_evt(false)); });
if (m_eventloop && m_running) { module->set_stop_cb([&] { enqueue(make_check_evt()); });
m_sig.emit(enqueue_update{eventloop_t::make_update_evt(false)});
}
});
module->set_stop_cb([&] {
if (m_eventloop && m_running) {
m_sig.emit(enqueue_check{eventloop::make_check_evt()});
}
});
module->setup(); module->setup();
m_eventloop->add_module(align, move(module)); m_modules[align].emplace_back(move(module));
} catch (const std::runtime_error& err) {
created_modules++;
} catch (const runtime_error& err) {
m_log.err("Disabling module \"%s\" (reason: %s)", module_name, err.what()); 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"); throw application_error("No modules created");
} }
} }
bool controller::run() { /**
assert(!m_connection.connection_has_error()); * Deconstruct controller
*/
m_log.info("Starting application"); controller::~controller() {
m_running = true; m_log.trace("controller: Uninstall sighandler");
signal(SIGINT, SIG_DFL);
m_sig.attach(this); signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
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);
m_log.trace("controller: Detach signal receiver");
m_sig.detach(this); m_sig.detach(this);
if (m_eventloop) { m_log.trace("controller: Stop modules");
// Signal the eventloop, in case it's still running for (auto&& block : m_modules) {
m_eventloop->enqueue(eventloop::make_quit_evt(false)); 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<int> 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) { if (m_ipc) {
m_ipc.reset(); fds.emplace_back((fd_ipc = m_ipc->get_file_descriptor()));
} }
if (!m_writeback && m_confwatch) { while (!g_terminate) {
m_log.trace("controller: Removing config watch"); fd_set readfds{};
m_confwatch->remove(true); FD_ZERO(&readfds);
}
return !m_running && !m_reload; int maxfd{0};
} for (auto&& fd : fds) {
FD_SET(fd, &readfds);
const bar_settings controller::opts() const { maxfd = std::max(maxfd, fd);
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);
} }
} 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() { // Wait until event is ready on one of the configured streams
m_log.trace("controller: Listen for X events"); int events = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr);
m_connection.flush();
while (m_running) { // Check for errors
try { if (events == -1 || g_terminate || m_connection.connection_has_error()) {
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()) {
break; break;
} }
}
if (m_running) { // Process event on the internal fd
kill(getpid(), SIGTERM); 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<xcb_generic_event_t> 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) { if (evt.type == static_cast<uint8_t>(event_type::INPUT)) {
m_log.trace("controller: eventloop ended, raising SIGALRM"); process_inputdata();
kill(getpid(), SIGALRM); } 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<uint8_t>(event_type::QUIT)) {
evt = next;
break;
} else if (next.type == static_cast<uint8_t>(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<uint8_t>(event_type::INPUT)) {
process_inputdata();
} else if (evt.type == static_cast<uint8_t>(event_type::QUIT)) {
m_sig.emit(sig_ev::process_quit{make_quit_evt(evt.flag)});
} else if (evt.type == static_cast<uint8_t>(event_type::UPDATE)) {
m_sig.emit(sig_ev::process_update{make_update_evt(evt.flag)});
} else if (evt.type == static_cast<uint8_t>(event_type::CHECK)) {
m_sig.emit(sig_ev::process_check{make_check_evt()});
} 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<decltype(m_swallow_input)>(chrono::system_clock::now());
m_inputdata.clear();
}
}
/**
* Process eventqueue update event
*/
bool controller::on(const sig_ev::process_update& evt) { bool controller::on(const sig_ev::process_update& evt) {
bool force{evt.data()->flag}; 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_left(bar.module_margin.left, ' ');
string margin_right(bar.module_margin.right, ' '); string margin_right(bar.module_margin.right, ' ');
for (const auto& block : m_eventloop->modules()) { for (const auto& block : m_modules) {
string block_contents; string block_contents;
bool is_left = false; bool is_left = false;
bool is_center = false; bool is_center = false;
@ -349,9 +431,22 @@ bool controller::on(const sig_ev::process_update& evt) {
return true; return true;
} }
/**
* Process eventqueue input event
*/
bool controller::on(const sig_ev::process_input& evt) { bool controller::on(const sig_ev::process_input& evt) {
try { 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) { if (m_command) {
m_log.warn("Terminating previous shell command"); m_log.warn("Terminating previous shell command");
@ -370,16 +465,35 @@ bool controller::on(const sig_ev::process_input& evt) {
return true; return true;
} }
bool controller::on(const sig_ev::process_quit&) { /**
kill(getpid(), SIGUSR1); * Process eventqueue quit event
return false; */
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) { * Process eventqueue check event
return false; */
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()}; string input{*evt.data()};
if (input.empty()) { if (input.empty()) {
@ -387,9 +501,13 @@ bool controller::on(const sig_ui::button_press& evt) {
return false; 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) { bool controller::on(const sig_ipc::process_action& evt) {
ipc_action a{*evt.data()}; ipc_action a{*evt.data()};
string action{a.payload}; 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); 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) { bool controller::on(const sig_ipc::process_command& evt) {
ipc_command c{*evt.data()}; ipc_command c{*evt.data()};
string command{c.payload}; string command{c.payload};
@ -414,9 +536,9 @@ bool controller::on(const sig_ipc::process_command& evt) {
} }
if (command == "quit") { if (command == "quit") {
m_eventloop->enqueue(eventloop::make_quit_evt(false)); enqueue(make_quit_evt(false));
} else if (command == "restart") { } else if (command == "restart") {
m_eventloop->enqueue(eventloop::make_quit_evt(true)); enqueue(make_quit_evt(true));
} else { } else {
m_log.warn("\"%s\" is not a valid ipc command", command); 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; return true;
} }
/**
* Process ipc hook messages
*/
bool controller::on(const sig_ipc::process_hook& evt) { bool controller::on(const sig_ipc::process_hook& evt) {
const ipc_hook hook{*evt.data()}; 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) { for (const auto& module : block.second) {
auto ipc = dynamic_cast<ipc_module*>(module.get()); auto ipc = dynamic_cast<ipc_module*>(module.get());
if (ipc != nullptr) { if (ipc != nullptr) {

View File

@ -1,294 +0,0 @@
#include <csignal>
#include <cstring>
#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<eventloop>(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<uint8_t>(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<uint8_t>(event_type::QUIT)) {
evt = next;
break;
} else if (next.type == static_cast<uint8_t>(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<uint8_t>(event_type::INPUT)) {
handle_inputdata();
} else if (evt.type == static_cast<uint8_t>(event_type::QUIT)) {
m_sig.emit(process_quit{make_input_evt()});
} else if (evt.type == static_cast<uint8_t>(event_type::UPDATE)) {
m_sig.emit(process_update{make_update_evt(evt.flag)});
} else if (evt.type == static_cast<uint8_t>(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<std::mutex> 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_t>(module));
} else {
vector<module_t> vec;
vec.emplace_back(forward<module_t>(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<std::mutex> guard(m_inputlock);
if (!m_inputdata.empty()) {
m_sig.emit(process_input{move(m_inputdata)});
m_lastinput = chrono::time_point_cast<decltype(m_swallow_input)>(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<const event>(*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<event>(*evt()));
}
bool eventloop::on(const enqueue_quit& evt) {
if (m_running) {
const event quit{reinterpret_cast<const event&>(*evt())};
m_log.info("Enqueuing QUIT event (reload=%i)", quit.flag);
return enqueue(static_cast<event>(*evt()));
}
return true;
}
bool eventloop::on(const enqueue_update& evt) {
event update{reinterpret_cast<const event&>(*evt())};
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<const event&>(*evt())};
m_log.trace("eventloop: enqueuing CHECK event");
return enqueue(move(check));
}
eventloop::event eventloop::make_quit_evt(bool reload) {
return event{static_cast<uint8_t>(event_type::QUIT), reload};
}
eventloop::event eventloop::make_update_evt(bool force) {
return event{static_cast<uint8_t>(event_type::UPDATE), force};
}
eventloop::event eventloop::make_input_evt() {
return event{static_cast<uint8_t>(event_type::INPUT)};
}
eventloop::event eventloop::make_check_evt() {
return event{static_cast<uint8_t>(event_type::CHECK)};
}
POLYBAR_NS_END

View File

@ -23,50 +23,49 @@ ipc::make_type ipc::make() {
} }
/** /**
* Interrupt the blocked listener and * Construct ipc handler
* remove the file 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() { ipc::~ipc() {
m_running = false; if (!m_path.empty()) {
m_log.trace("ipc: Removing file handle");
if (!m_fifo.empty()) { unlink(m_path.c_str());
m_log.info("Interrupting ipc message receiver");
auto f = make_unique<file_util::file_ptr>(m_fifo);
char p[1]{'q'};
fwrite(p, sizeof(char), sizeof(p), (*f)());
unlink(m_fifo.c_str());
} }
} }
/** /**
* Start listening for event messages * Receive available ipc messages and delegate valid events
*/ */
void ipc::receive_messages() { void ipc::receive_message() {
m_running = true; m_log.info("Receiving ipc message");
m_fifo = string_util::replace(PATH_MESSAGING_FIFO, "%pid%", to_string(getpid()));
if (mkfifo(m_fifo.c_str(), 0666) == -1) { char buffer[BUFSIZ]{'\0'};
m_log.err("Failed to create messaging channel"); 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); if (!bytes_read) {
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()) {
return; 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{}; ipc_command msg{};
memcpy(msg.payload, &payload[0], payload.size()); memcpy(msg.payload, &payload[0], payload.size());
m_sig.emit(process_command{move(msg)}); m_sig.emit(process_command{move(msg)});
@ -78,9 +77,18 @@ void ipc::parse(const string& payload) const {
ipc_action msg{}; ipc_action msg{};
memcpy(msg.payload, &payload[0], payload.size()); memcpy(msg.payload, &payload[0], payload.size());
m_sig.emit(process_action{move(msg)}); m_sig.emit(process_action{move(msg)});
} else { } else if (!payload.empty()) {
m_log.warn("Received unknown ipc message: (payload=%s)", payload); 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 POLYBAR_NS_END

View File

@ -12,20 +12,27 @@ POLYBAR_NS
using namespace signals::parser; using namespace signals::parser;
/**
* Create instance
*/
parser::make_type parser::make() {
return factory_util::unique<parser>(signal_emitter::make());
}
/** /**
* Construct parser instance * 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 * Process input string
*/ */
void parser::operator()(string data) { void parser::parse(const bar_settings& bar, string data) {
while (!data.empty()) { while (!data.empty()) {
size_t pos{string::npos}; size_t pos{string::npos};
if (data.compare(0, 2, "%{") == 0 && (pos = data.find('}')) != 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); data.erase(0, pos + 1);
} else if ((pos = data.find("%{")) != string::npos) { } else if ((pos = data.find("%{")) != string::npos) {
data.erase(0, text(data.substr(0, pos))); data.erase(0, text(data.substr(0, pos)));
@ -42,7 +49,7 @@ void parser::operator()(string data) {
/** /**
* Process contents within tag blocks, i.e: %{...} * Process contents within tag blocks, i.e: %{...}
*/ */
void parser::codeblock(string&& data) { void parser::codeblock(string&& data, const bar_settings& bar) {
size_t pos; size_t pos;
while (data.length()) { while (data.length()) {
@ -66,11 +73,11 @@ void parser::codeblock(string&& data) {
switch (tag) { switch (tag) {
case 'B': case 'B':
m_sig.emit(change_background{parse_color(value, m_bar.background)}); m_sig.emit(change_background{parse_color(value, bar.background)});
break; break;
case 'F': case 'F':
m_sig.emit(change_foreground{parse_color(value, m_bar.foreground)}); m_sig.emit(change_foreground{parse_color(value, bar.foreground)});
break; break;
case 'T': case 'T':
@ -78,21 +85,21 @@ void parser::codeblock(string&& data) {
break; break;
case 'U': 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)});
m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)}); m_sig.emit(change_overline{parse_color(value, bar.overline.color)});
break; break;
case 'u': 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; break;
case 'o': 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; break;
case 'R': case 'R':
m_sig.emit(change_background{parse_color(value, m_bar.foreground)}); m_sig.emit(change_background{parse_color(value, bar.foreground)});
m_sig.emit(change_foreground{parse_color(value, m_bar.background)}); m_sig.emit(change_foreground{parse_color(value, bar.background)});
break; break;
case 'O': case 'O':

View File

@ -2,7 +2,6 @@
#include <thread> #include <thread>
#include "components/config.hpp" #include "components/config.hpp"
#include "components/eventloop.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "components/screen.hpp" #include "components/screen.hpp"
#include "components/types.hpp" #include "components/types.hpp"
@ -14,7 +13,7 @@
POLYBAR_NS POLYBAR_NS
using namespace signals::eventloop; using namespace signals::eventqueue;
/** /**
* Create instance * 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_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; m_sigraised = true;
} }

View File

@ -1,21 +1,25 @@
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include "common.hpp" #include "common.hpp"
#include "components/bar.hpp"
#include "components/command_line.hpp" #include "components/command_line.hpp"
#include "components/config.hpp" #include "components/config.hpp"
#include "components/controller.hpp" #include "components/controller.hpp"
#include "components/ipc.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "components/parser.hpp"
#include "components/renderer.hpp"
#include "config.hpp" #include "config.hpp"
#include "utils/env.hpp" #include "utils/env.hpp"
#include "utils/file.hpp"
#include "utils/inotify.hpp" #include "utils/inotify.hpp"
#include "utils/process.hpp" #include "utils/process.hpp"
#include "x11/connection.hpp"
#include "x11/tray_manager.hpp"
#include "x11/xutils.hpp" #include "x11/xutils.hpp"
using namespace polybar; using namespace polybar;
struct exit_success {};
struct exit_failure {};
int main(int argc, char** argv) { int main(int argc, char** argv) {
// clang-format off // clang-format off
const command_line::options opts{ const command_line::options opts{
@ -41,27 +45,19 @@ int main(int argc, char** argv) {
// Connect to X server // Connect to X server
//================================================== //==================================================
XInitThreads(); XInitThreads();
shared_ptr<xcb_connection_t> xcbconn{xutils::get_connection()};
// Store the xcb connection pointer with a disconnect deleter
shared_ptr<xcb_connection_t> xcbconn{xutils::get_connection().get(), xutils::xcb_connection_deleter{}};
if (!xcbconn) { if (!xcbconn) {
logger.err("A connection to X could not be established... "); logger.err("A connection to X could not be established... ");
throw exit_failure{}; return EXIT_FAILURE;
} }
connection conn{xcbconn.get()}; connection conn{xcbconn.get()};
conn.preload_atoms(); conn.preload_atoms();
conn.query_extensions(); 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 // Parse command line arguments
//================================================== //==================================================
@ -79,13 +75,13 @@ int main(int argc, char** argv) {
if (cli->has("help")) { if (cli->has("help")) {
cli->usage(); cli->usage();
throw exit_success{}; return EXIT_SUCCESS;
} else if (cli->has("version")) { } else if (cli->has("version")) {
print_build_info(version_details(args)); print_build_info(version_details(args));
throw exit_success{}; return EXIT_SUCCESS;
} else if (args.empty() || args[0][0] == '-') { } else if (args.empty() || args[0][0] == '-') {
cli->usage(); cli->usage();
throw exit_failure{}; return EXIT_FAILURE;
} }
//================================================== //==================================================
@ -110,62 +106,45 @@ int main(int argc, char** argv) {
//================================================== //==================================================
if (cli->has("dump")) { if (cli->has("dump")) {
std::cout << conf.get<string>(conf.section(), cli->get("dump")) << std::endl; std::cout << conf.get<string>(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 // Create controller and run application
//================================================== //==================================================
string path_confwatch; unique_ptr<ipc> ipc{};
bool enable_ipc{false}; unique_ptr<inotify_watch> config_watch{};
if (!cli->has("print-wmname")) { if (conf.get<bool>(conf.section(), "enable-ipc", false)) {
enable_ipc = conf.get<bool>(conf.section(), "enable-ipc", false); ipc = ipc::make();
} }
if (!cli->has("print-wmname") && cli->has("reload")) { if (cli->has("reload")) {
path_confwatch = conf.filepath(); config_watch = inotify_util::make_watch(conf.filepath());
} }
unique_ptr<controller> 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")) { if (!ctrl->run(cli->has("stdout"))) {
std::cout << ctrl->opts().wmname << std::endl;
throw exit_success{};
}
ctrl->setup();
if (!ctrl->run()) {
reload = true; 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) { } catch (const exception& err) {
logger.err(err.what()); logger.err(err.what());
exit_code = EXIT_FAILURE; exit_code = EXIT_FAILURE;
} }
if (!reload) { logger.info("Waiting for spawned processes to end");
logger.info("Reached end of application..."); while (process_util::notify_childprocess())
return exit_code; ;
}
try { if (reload) {
logger.warn("Re-launching application...");
logger.info("Re-launching application..."); logger.info("Re-launching application...");
process_util::exec(move(argv[0]), move(argv)); 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;
} }

View File

@ -257,8 +257,10 @@ namespace modules {
* Get the current capacity level * Get the current capacity level
*/ */
int battery_module::current_percentage() { 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_now =
auto capacity_max = std::strtoul(file_util::get_contents(m_valuepath[battery_value::CAPACITY_MAX]).c_str(), nullptr, 10); 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); auto percentage = math_util::percentage(capacity_now, 0UL, capacity_max);
return percentage < m_fullat ? percentage : 100; return percentage < m_fullat ? percentage : 100;

View File

@ -85,6 +85,7 @@ namespace modules {
void volume_module::teardown() { void volume_module::teardown() {
m_mixer.clear(); m_mixer.clear();
m_ctrl.clear(); m_ctrl.clear();
snd_config_update_free_global();
} }
bool volume_module::has_event() { bool volume_module::has_event() {

View File

@ -100,9 +100,10 @@ namespace modules {
*/ */
bool xkeyboard_module::build(builder* builder, const string& tag) const { bool xkeyboard_module::build(builder* builder, const string& tag) const {
if (tag == TAG_LABEL_LAYOUT) { 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->node(m_layout);
builder->cmd_close(); builder->cmd_close(precond);
} else if (tag == TAG_LABEL_INDICATOR) { } else if (tag == TAG_LABEL_INDICATOR) {
size_t n{0}; size_t n{0};
for (auto&& indicator : m_indicators) { for (auto&& indicator : m_indicators) {
@ -139,6 +140,8 @@ namespace modules {
m_keyboard->current(current_group); m_keyboard->current(current_group);
m_connection.flush(); m_connection.flush();
update();
return true; return true;
} }

View File

@ -10,196 +10,194 @@
POLYBAR_NS POLYBAR_NS
namespace command_util { command::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(cmd)) {
command::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(cmd)) { if (pipe(m_stdin) != 0) {
if (pipe(m_stdin) != 0) { throw command_error("Failed to allocate input stream");
throw command_strerror("Failed to allocate input stream");
}
if (pipe(m_stdout) != 0) {
throw command_strerror("Failed to allocate output stream");
}
} }
if (pipe(m_stdout) != 0) {
command::~command() { throw command_error("Failed to allocate output stream");
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<string> cb) {
io_util::tail(m_stdout[PIPE_READ], move(cb));
}
/**
* Write line to command input channel
*/
int command::writeline(string data) {
std::lock_guard<concurrency_util::spin_lock> 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<concurrency_util::spin_lock> 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;
} }
} }
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<string> cb) {
io_util::tail(m_stdout[PIPE_READ], move(cb));
}
/**
* Write line to command input channel
*/
int command::writeline(string data) {
std::lock_guard<std::mutex> 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<std::mutex> 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 POLYBAR_NS_END

8
src/utils/factory.cpp Normal file
View File

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

View File

@ -10,30 +10,67 @@
POLYBAR_NS 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 { 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 * Checks if the given file exist
*/ */

View File

@ -1,123 +1,123 @@
#include <unistd.h> #include <unistd.h>
#include "errors.hpp" #include "errors.hpp"
#include "utils/factory.hpp"
#include "utils/inotify.hpp" #include "utils/inotify.hpp"
#include "utils/memory.hpp" #include "utils/memory.hpp"
POLYBAR_NS POLYBAR_NS
namespace inotify_util { /**
/** * Construct inotify watch
* Construct instance */
*/ inotify_watch::inotify_watch(string path) : m_path(move(path)) {}
inotify_watch::inotify_watch(string path) : m_path(move(path)) {}
/** /**
* Destructor * Deconstruct inotify watch
*/ */
inotify_watch::~inotify_watch() noexcept { inotify_watch::~inotify_watch() {
if (m_wd != -1) { if (m_wd != -1) {
inotify_rm_watch(m_fd, m_wd); inotify_rm_watch(m_fd, m_wd);
}
if (m_fd != -1) {
close(m_fd);
}
} }
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<event_t> inotify_watch::get_event() {
auto event = factory_util::unique<event_t>();
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<watch_t::element_type>(move(path));
} }
} }
/**
* 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_event> inotify_watch::get_event() {
auto event = factory_util::unique<inotify_event>();
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 POLYBAR_NS_END

View File

@ -160,21 +160,6 @@ void connection::send_client_message(const shared_ptr<xcb_client_message_event_t
flush(); flush();
} }
/**
* Sends a dummy event to the specified window
* Used to interrupt blocking wait call
*
* @XXX: Find the proper way to interrupt the blocking wait
* except the obvious event polling
*/
void connection::send_dummy_event(xcb_window_t target, uint32_t event) const {
if (target == XCB_NONE) {
target = root();
}
auto message = make_client_message(XCB_VISIBILITY_NOTIFY, target);
send_client_message(message, target, event);
}
/** /**
* Try to get a visual type for the given screen that * Try to get a visual type for the given screen that
* matches the given depth * matches the given depth

View File

@ -1,5 +1,5 @@
#include <utility>
#include <algorithm> #include <algorithm>
#include <utility>
#include "components/types.hpp" #include "components/types.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"

View File

@ -3,7 +3,6 @@
#include <thread> #include <thread>
#include "components/config.hpp" #include "components/config.hpp"
#include "components/eventloop.hpp"
#include "components/types.hpp" #include "components/types.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "events/signal.hpp" #include "events/signal.hpp"
@ -284,7 +283,7 @@ void tray_manager::deactivate(bool clear_selection) {
m_connection.flush(); 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_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_client> tray_manager::find_client(const xcb_window_t& win) const { shared_ptr<tray_client> tray_manager::find_client(const xcb_window_t& win) const {
for (auto&& client : m_clients) { for (auto&& client : m_clients) {
if (client->match(win)) { if (client->match(win)) {
return shared_ptr<tray_client>{client.get(), factory_util::null_deleter{}}; return shared_ptr<tray_client>{client.get(), factory_util::null_deleter};
} }
} }
return nullptr; return nullptr;

View File

@ -38,7 +38,8 @@ namespace wm_util {
} }
void set_wm_window_opacity(xcb_connection_t* conn, xcb_window_t win, uint64_t values) { 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) { if (reply) {
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, reply->atom, XCB_ATOM_CARDINAL, 32, 1, &values); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, reply->atom, XCB_ATOM_CARDINAL, 32, 1, &values);
xcb_flush(conn); xcb_flush(conn);

View File

@ -9,7 +9,7 @@ namespace xlib {
shared_ptr<Display> get_display() { shared_ptr<Display> get_display() {
if (!g_display_ptr) { if (!g_display_ptr) {
g_display_ptr = shared_ptr<Display>(XOpenDisplay(nullptr), [=](Display* ptr) { XCloseDisplay(ptr); }); g_display_ptr = shared_ptr<Display>(XOpenDisplay(nullptr), factory_util::null_deleter);
} }
return g_display_ptr; return g_display_ptr;
} }

View File

@ -19,8 +19,7 @@ namespace xutils {
if (dsp) { if (dsp) {
XSetEventQueueOwner(dsp.get(), XCBOwnsEventQueue); XSetEventQueueOwner(dsp.get(), XCBOwnsEventQueue);
g_connection_ptr = g_connection_ptr = shared_ptr<xcb_connection_t>(XGetXCBConnection(dsp.get()), factory_util::null_deleter);
shared_ptr<xcb_connection_t>(XGetXCBConnection(dsp.get()), [=](xcb_connection_t* c) { xcb_disconnect(c); });
} }
} }
@ -30,7 +29,7 @@ namespace xutils {
int get_connection_fd() { int get_connection_fd() {
if (!g_connection_fd) { if (!g_connection_fd) {
auto fd = xcb_get_file_descriptor(get_connection().get()); auto fd = xcb_get_file_descriptor(get_connection().get());
g_connection_fd = shared_ptr<int>(new int{fd}, factory_util::fd_deleter{}); g_connection_fd = shared_ptr<int>(new int{fd}, factory_util::fd_deleter);
} }
return *g_connection_fd.get(); return *g_connection_fd.get();