#pragma once #include "modules/meta.hpp" #include "utils/command.hpp" LEMONBUDDY_NS #define SHELL_CMD "/usr/bin/env\nsh\n-c\n" #define OUTPUT_ACTION(BUTTON) \ if (!m_actions[BUTTON].empty()) \ m_builder->cmd(BUTTON, string_util::replace_all(m_actions[BUTTON], "%counter%", counter_str)) namespace modules { class script_module : public event_module { public: using event_module::event_module; void setup() { m_formatter->add(DEFAULT_FORMAT, TAG_OUTPUT, {TAG_OUTPUT}); // Load configuration values {{{ REQ_CONFIG_VALUE(name(), m_exec, "exec"); GET_CONFIG_VALUE(name(), m_tail, "tail"); GET_CONFIG_VALUE(name(), m_maxlen, "maxlen"); GET_CONFIG_VALUE(name(), m_ellipsis, "ellipsis"); m_actions[mousebtn::LEFT] = m_conf.get(name(), "click-left", ""); m_actions[mousebtn::MIDDLE] = m_conf.get(name(), "click-middle", ""); m_actions[mousebtn::RIGHT] = m_conf.get(name(), "click-right", ""); m_actions[mousebtn::SCROLL_UP] = m_conf.get(name(), "scroll-up", ""); m_actions[mousebtn::SCROLL_DOWN] = m_conf.get(name(), "scroll-down", ""); if (!m_tail) { m_interval = interval_t{m_conf.get(name(), "interval", m_interval.count())}; } // }}} // Execute the tail command {{{ if (m_tail) { try { auto exec = string_util::replace_all(m_exec, "%counter%", to_string(++m_counter)); m_log.trace("%s: Executing '%s'", name(), exec); m_command = command_util::make_command(SHELL_CMD + exec); m_command->exec(false); } catch (const std::exception& err) { m_log.err("%s: Failed to execute tail command, stopping module..", name()); m_log.err("%s: %s", name(), err.what()); stop(); } } // }}} } void stop() { // Put the module in stopped state {{{ event_module::stop(); // }}} // Terminate running command {{{ try { if (m_tail && m_command) m_command.reset(); } catch (const std::exception& err) { m_log.err("%s: %s", name(), err.what()); } // }}} } bool has_event() { // Handle non-tailing command {{{ if (!m_tail) { sleep(m_interval); return enabled(); } // }}} // Handle tailing command {{{ if (!m_command || !m_command->is_running()) { m_log.warn("%s: Tail command finished, stopping module...", name()); stop(); return false; } else if ((m_output = m_command->readline()) != m_prev) { m_prev = m_output; return true; } else { return false; } // }}} } bool update() { // Handle tailing command {{{ if (m_tail) return true; // }}} // Handle non-tailing command {{{ try { auto exec = string_util::replace_all(m_exec, "%counter%", to_string(++m_counter)); auto cmd = command_util::make_command(SHELL_CMD + exec); m_log.trace("%s: Executing '%s'", name(), exec); cmd->exec(); cmd->tail([this](string contents) { m_output = contents; }); } catch (const std::exception& err) { m_log.err("%s: Failed to execute command, stopping module..", name()); m_log.err("%s: %s", name(), err.what()); stop(); return false; } if (m_output != m_prev) { m_prev = m_output; return true; } return false; // }}} } string get_output() { if (m_output.empty()) return " "; // Truncate output to the defined max length {{{ if (m_maxlen > 0 && m_output.length() > m_maxlen) { m_output.erase(m_maxlen); m_output += m_ellipsis ? "..." : ""; } // }}} // Add mousebtn command handlers {{{ auto counter_str = to_string(m_counter); OUTPUT_ACTION(mousebtn::LEFT); OUTPUT_ACTION(mousebtn::MIDDLE); OUTPUT_ACTION(mousebtn::RIGHT); OUTPUT_ACTION(mousebtn::SCROLL_UP); OUTPUT_ACTION(mousebtn::SCROLL_DOWN); // }}} m_builder->node(module::get_output()); return m_builder->flush(); } bool build(builder* builder, string tag) { if (tag != TAG_OUTPUT) return false; builder->node(string_util::replace_all(m_output, "\n", "")); return true; } protected: static constexpr auto TAG_OUTPUT = ""; command_util::command_t m_command; string m_exec; bool m_tail = false; interval_t m_interval = 1s; size_t m_maxlen = 0; bool m_ellipsis = true; map m_actions; string m_output; string m_prev; int m_counter{0}; }; } LEMONBUDDY_NS_END