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