diff --git a/cmake/build/options.cmake b/cmake/build/options.cmake index 3fb9a1e8..e6d61c85 100644 --- a/cmake/build/options.cmake +++ b/cmake/build/options.cmake @@ -79,5 +79,7 @@ set(SETTING_PATH_MEMORY_INFO "/proc/meminfo" CACHE STRING "Path to file containing memory info") set(SETTING_PATH_TEMPERATURE_INFO "/sys/class/thermal/thermal_zone%zone%/temp" CACHE STRING "Path to file containing the current temperature") +set(SETTING_PATH_MESSAGING_FIFO "/tmp/lemonbuddy_mqueue.%pid%" + CACHE STRING "Path to file containing the current temperature") # }}} diff --git a/include/components/controller.hpp b/include/components/controller.hpp index 70643ebd..6978a97f 100644 --- a/include/components/controller.hpp +++ b/include/components/controller.hpp @@ -4,8 +4,8 @@ #include "components/bar.hpp" #include "components/config.hpp" #include "components/eventloop.hpp" +#include "components/ipc.hpp" #include "components/logger.hpp" -#include "components/signals.hpp" #include "config.hpp" #include "utils/command.hpp" #include "utils/inotify.hpp" @@ -17,12 +17,13 @@ LEMONBUDDY_NS class controller { public: explicit controller(connection& conn, const logger& logger, const config& config, unique_ptr eventloop, - unique_ptr bar, inotify_util::watch_t& confwatch) + unique_ptr bar, unique_ptr ipc, inotify_util::watch_t& confwatch) : m_connection(conn) , m_log(logger) , m_conf(config) , m_eventloop(forward(eventloop)) , m_bar(forward(bar)) + , m_ipc(forward(ipc)) , m_confwatch(confwatch) {} ~controller(); @@ -53,6 +54,7 @@ class controller { const config& m_conf; unique_ptr m_eventloop; unique_ptr m_bar; + unique_ptr m_ipc; stateflag m_running{false}; stateflag m_reload{false}; @@ -67,7 +69,7 @@ class controller { inotify_util::watch_t& m_confwatch; command_util::command_t m_command; - bool m_writeback = false; + bool m_writeback{false}; }; namespace { @@ -83,7 +85,8 @@ namespace { configure_logger(), configure_config(), configure_eventloop(), - configure_bar()); + configure_bar(), + configure_ipc()); // clang-format on } } diff --git a/include/components/ipc.hpp b/include/components/ipc.hpp new file mode 100644 index 00000000..a675e028 --- /dev/null +++ b/include/components/ipc.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "common.hpp" +#include "components/logger.hpp" + +LEMONBUDDY_NS + +/** + * Component used for inter-process communication. + * + * A unique messaging channel will be setup for each + * running process which will allow messages and + * events to be sent to the process externally. + */ +class ipc { + public: + struct message_internal { + static constexpr auto prefix{"app:"}; + }; + struct message_command { + static constexpr auto prefix{"cmd:"}; + }; + struct message_custom { + static constexpr auto prefix{"custom:"}; + }; + + explicit ipc(const logger& logger) : m_log(logger) {} + ~ipc(); + + void receive_messages(); + + protected: + void parse(string payload); + + private: + const logger& m_log; + + stateflag m_running{false}; + + string m_fifo; + int m_fd; +}; + +namespace { + /** + * Configure injection module + */ + template > + di::injector configure_ipc() { + return di::make_injector(configure_logger()); + } +} + +LEMONBUDDY_NS_END diff --git a/include/config.hpp.cmake b/include/config.hpp.cmake index c781dfaf..57080092 100644 --- a/include/config.hpp.cmake +++ b/include/config.hpp.cmake @@ -39,6 +39,7 @@ #define PATH_CPU_INFO "@SETTING_PATH_CPU_INFO@" #define PATH_MEMORY_INFO "@SETTING_PATH_MEMORY_INFO@" #define PATH_TEMPERATURE_INFO "@SETTING_PATH_TEMPERATURE_INFO@" +#define PATH_MESSAGING_FIFO "@SETTING_PATH_MESSAGING_FIFO@" auto print_build_info = []() { // clang-format off diff --git a/src/components/controller.cpp b/src/components/controller.cpp index 20cd1f3a..5f5af042 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -2,6 +2,12 @@ #include #include "components/controller.hpp" + +#include "components/bar.hpp" +#include "components/config.hpp" +#include "components/eventloop.hpp" +#include "components/ipc.hpp" +#include "components/logger.hpp" #include "components/signals.hpp" #include "modules/backlight.hpp" #include "modules/battery.hpp" @@ -56,6 +62,11 @@ controller::~controller() { m_eventloop.reset(); } + if (m_ipc) { + m_log.info("Deconstructing ipc"); + m_ipc.reset(); + } + if (m_bar) { m_log.info("Deconstructing bar"); m_bar.reset(); @@ -94,30 +105,23 @@ void controller::bootstrap(bool writeback, bool dump_wmname) { // Listen for events on the root window to be able to // break the blocking wait call when cleaning up m_log.trace("controller: Listen for events on the root window"); - try { - const uint32_t value_list[2]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY}; - m_connection.change_window_attributes_checked(m_connection.root(), XCB_CW_EVENT_MASK, value_list); - } catch (const exception& err) { - throw application_error("Failed to change root window event mask: " + string{err.what()}); - } + const uint32_t value_list[2]{XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY}; + m_connection.change_window_attributes_checked(m_connection.root(), XCB_CW_EVENT_MASK, value_list); - try { - m_log.trace("controller: Setup bar"); - m_bar->bootstrap(m_writeback || dump_wmname); - m_bar->bootstrap_tray(); - } catch (const exception& err) { - throw application_error("Failed to setup bar renderer: " + string{err.what()}); - } + m_log.trace("controller: Setup bar"); + m_bar->bootstrap(m_writeback || dump_wmname); + m_bar->bootstrap_tray(); if (dump_wmname) { std::cout << m_bar->settings().wmname << std::endl; return; } - m_log.trace("controller: Attach eventloop callbacks"); + m_log.trace("controller: Attach eventloop update callback"); m_eventloop->set_update_cb(bind(&controller::on_update, this)); if (!m_writeback) { + m_log.trace("controller: Attach eventloop input callback"); g_signals::bar::action_click = bind(&controller::on_mouse_event, this, placeholders::_1); m_eventloop->set_input_db(bind(&controller::on_unrecognized_action, this, placeholders::_1)); } @@ -138,6 +142,11 @@ bool controller::run() { install_sigmask(); install_confwatch(); +#if DEBUG + // Start listening for ipc messages + m_threads.emplace_back(thread(&ipc::receive_messages, m_ipc.get())); +#endif + // Listen for X events in separate thread if (!m_writeback) { m_threads.emplace_back(thread(&controller::wait_for_xevent, this)); diff --git a/src/components/ipc.cpp b/src/components/ipc.cpp new file mode 100644 index 00000000..fb7d851b --- /dev/null +++ b/src/components/ipc.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include "components/ipc.hpp" +#include "config.hpp" +#include "utils/file.hpp" +#include "utils/io.hpp" +#include "utils/string.hpp" + +LEMONBUDDY_NS + +/** + * Interrupt the blocked listener and + * remove the file handler + */ +ipc::~ipc() { + m_running = false; + + if (!m_fifo.empty()) { + m_log.info("Interrupting ipc message receiver"); + + auto f{make_unique(m_fifo)}; + char p[1]{'q'}; + + fwrite(p, sizeof(char), sizeof(p), (*f)()); + unlink(m_fifo.c_str()); + } +} + +/** + * Start listening for event messages + */ +void ipc::receive_messages() { + m_running = true; + m_fifo = string_util::replace(PATH_MESSAGING_FIFO, "%pid%", to_string(getpid())); + + if (mkfifo(m_fifo.c_str(), 0666) == -1) { + m_log.err("Failed to create messaging channel"); + } + + 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(string payload) { + if (payload.empty()) { + return; + } else if (payload.find(message_internal::prefix) == 0) { + m_log.info("Received internal message: (payload=%s)", payload); + } else if (payload.find(message_command::prefix) == 0) { + m_log.info("Received command message: (payload=%s)", payload); + } else if (payload.find(message_custom::prefix) == 0) { + m_log.info("Received custom message: (payload=%s)", payload); + } else { + m_log.info("Received unknown message: (payload=%s)", payload); + } +} + +LEMONBUDDY_NS_END diff --git a/src/utils/io.cpp b/src/utils/io.cpp index 59865331..0bda3903 100644 --- a/src/utils/io.cpp +++ b/src/utils/io.cpp @@ -37,26 +37,24 @@ namespace io_util { } string readline(int read_fd, int& bytes_read) { - std::stringstream buffer; + stringstream buffer; char char_; - + int bytes = 0; bytes_read = 0; - while ((bytes_read += ::read(read_fd, &char_, 1)) > 0) { - buffer << char_; + while ((bytes = ::read(read_fd, &char_, 1)) > 0) { + if (bytes <= 0) + break; if (char_ == '\n' || char_ == '\x00') break; + bytes_read += bytes; + buffer << char_; } - if (bytes_read == 0) { - // Reached EOF - } else if (bytes_read == -1) { - // Read failed - } else { - return string_util::strip_trailing_newline(buffer.str()); - } + if (bytes_read <= 0) + return ""; - return ""; + return string_util::strip_trailing_newline(buffer.str()); } string readline(int read_fd) {