#include #include #include "components/config.hpp" #include "components/eventloop.hpp" #include "components/logger.hpp" #include "components/types.hpp" #include "events/signal.hpp" #include "modules/meta/base.hpp" #include "utils/factory.hpp" #include "utils/string.hpp" #include "utils/time.hpp" #include "x11/color.hpp" POLYBAR_NS /** * Create instance */ eventloop::make_type eventloop::make() { return factory_util::unique(signal_emitter::make(), logger::make(), config::make()); } /** * Construct eventloop instance */ eventloop::eventloop(signal_emitter& emitter, const logger& logger, const config& config) : m_sig(emitter), m_log(logger), m_conf(config) { m_swallow_limit = m_conf.deprecated("settings", "eventloop-swallow", "throttle-output", m_swallow_limit); m_swallow_update = m_conf.deprecated( "settings", "eventloop-swallow-time", "throttle-output-for", m_swallow_update); m_swallow_input = m_conf.get("settings", "throttle-input-for", m_swallow_update * m_swallow_limit); m_sig.attach(this); } /** * Deconstruct eventloop */ eventloop::~eventloop() { m_sig.detach(this); for (auto&& block : m_modules) { for (auto&& module : block.second) { auto module_name = module->name(); auto cleanup_ms = time_util::measure([&module] { module->stop(); module.reset(); }); m_log.info("eventloop: Deconstruction of %s took %lu microsec.", module_name, cleanup_ms); } } } /** * Start module and worker threads */ void eventloop::start() { m_log.info("Starting event loop"); m_running = true; dispatch_modules(); while (m_running) { event evt, next; m_queue.wait_dequeue(evt); if (!m_running) { break; } if (evt.type == static_cast(event_type::INPUT)) { handle_inputdata(); } else { size_t swallowed{0}; while (swallowed++ < m_swallow_limit && m_queue.wait_dequeue_timed(next, m_swallow_update)) { if (next.type == static_cast(event_type::QUIT)) { evt = next; break; } else if (next.type == static_cast(event_type::INPUT)) { evt = next; break; } else if (evt.type != next.type) { m_queue.try_enqueue(move(next)); break; } else { m_log.trace_x("eventloop: Swallowing event within timeframe"); evt = next; } } if (evt.type == static_cast(event_type::INPUT)) { handle_inputdata(); } else if (evt.type == static_cast(event_type::QUIT)) { m_sig.emit(process_quit{reinterpret_cast(evt)}); } else if (evt.type == static_cast(event_type::UPDATE)) { m_sig.emit(process_update{reinterpret_cast(evt)}); } else if (evt.type == static_cast(event_type::CHECK)) { m_sig.emit(process_check{reinterpret_cast(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) { uint8_t type{static_cast(evt.type)}; if (!m_queue.enqueue(move(evt))) { m_log.warn("Failed to enqueue event (%d)", static_cast(type)); return false; } return true; } /** * Enqueue input data */ bool eventloop::enqueue(string&& input_data) { if (m_inputlock.try_lock()) { return false; } std::lock_guard guard(m_inputlock, std::adopt_lock); if (!m_inputdata.empty()) { m_log.trace("eventloop: Swallowing input event (pending data)"); return false; } else if (chrono::system_clock::now() - m_swallow_input < m_lastinput) { m_log.trace("eventloop: Swallowing input event (throttled)"); return false; } m_inputdata = forward(input_data); return enqueue(make_input_evt()); } /** * Add module to alignment block */ void eventloop::add_module(const alignment pos, module_t&& module) { auto it = m_modules.lower_bound(pos); if (it != m_modules.end() && !(m_modules.key_comp()(pos, it->first))) { it->second.emplace_back(forward(module)); } else { vector vec; vec.emplace_back(forward(module)); m_modules.insert(it, modulemap_t::value_type(pos, move(vec))); } } /** * Get reference to module map */ const modulemap_t& eventloop::modules() const { return m_modules; } /** * Get loaded module count */ size_t eventloop::module_count() const { size_t count{0}; for (auto&& block : m_modules) { count += block.second.size(); } return count; } /** * Start module threads */ void eventloop::dispatch_modules() { for (const auto& block : m_modules) { for (const auto& module : block.second) { try { m_log.info("Starting %s", module->name()); module->start(); } catch (const application_error& err) { m_log.err("Failed to start '%s' (reason: %s)", module->name(), err.what()); } } } } /** * Process pending input data */ void eventloop::handle_inputdata() { std::lock_guard guard(m_inputlock); if (!m_inputdata.empty()) { m_sig.emit(process_input{move(m_inputdata)}); m_lastinput = chrono::time_point_cast(chrono::system_clock::now()); m_inputdata.clear(); } } bool eventloop::on(const process_input& evt) { string input{*evt()}; for (auto&& block : m_modules) { for (auto&& module : block.second) { if (module->receive_events() && module->handle_event(input)) { return true; } } } m_log.warn("Input event \"%s\" was rejected by all modules, passing to shell...", input); return false; } bool eventloop::on(const process_check&) { for (const auto& block : m_modules) { for (const auto& module : block.second) { if (m_running && module->running()) { return true; } } } m_log.warn("No running modules..."); enqueue(make_quit_evt(false)); return true; } bool eventloop::on(const process_quit& evt) { assert((*evt()).type == static_cast(event_type::QUIT)); const event quit{static_cast(*evt())}; m_log.info("Processing QUIT event (reload=%i)", quit.flag); m_running = false; return !quit.flag; // break emit chain if reload flag isn't set } bool eventloop::on(const enqueue_event& evt) { m_log.trace("eventloop: enqueuing event (type=%i)", (*evt()).type); return enqueue(static_cast(*evt())); } bool eventloop::on(const enqueue_quit& evt) { assert((*evt()).type == static_cast(event_type::QUIT)); if (m_running) { const event quit{reinterpret_cast(*evt())}; m_log.info("Enqueuing QUIT event (reload=%i)", quit.flag); return enqueue(static_cast(*evt())); } return true; } bool eventloop::on(const enqueue_update& evt) { event update{reinterpret_cast(*evt())}; assert(update.type == static_cast(event_type::UPDATE)); m_log.trace("eventloop: enqueuing UPDATE event (force=%i)", update.flag); return enqueue(move(update)); } bool eventloop::on(const enqueue_input& evt) { m_log.trace("eventloop: enqueuing INPUT event"); return enqueue(string{move(*evt())}); } bool eventloop::on(const enqueue_check& evt) { event check{reinterpret_cast(*evt())}; assert(check.type == static_cast(event_type::CHECK)); m_log.trace("eventloop: enqueuing CHECK event"); return enqueue(move(check)); } eventloop::event eventloop::make_quit_evt(bool reload) { return event{static_cast(event_type::QUIT), reload}; } eventloop::event eventloop::make_update_evt(bool force) { return event{static_cast(event_type::UPDATE), force}; } eventloop::event eventloop::make_input_evt() { return event{static_cast(event_type::INPUT)}; } eventloop::event eventloop::make_check_evt() { return event{static_cast(event_type::CHECK)}; } POLYBAR_NS_END