1 #include "modules/script.hpp" 2 #include "drawtypes/label.hpp" 3 #include "modules/meta/base.inl" 4 5 POLYBAR_NS 6 7 namespace modules { 8 template class module<script_module>; 9 10 /** 11 * Construct script module by loading configuration values 12 * and setting up formatting objects 13 */ script_module(const bar_settings & bar,string name_)14 script_module::script_module(const bar_settings& bar, string name_) 15 : module<script_module>(bar, move(name_)), m_handler([&]() -> function<chrono::duration<double>()> { 16 17 m_tail = m_conf.get(name(), "tail", false); 18 // Handler for continuous tail commands {{{ 19 20 if (m_tail) { 21 return [&] { 22 if (!m_command || !m_command->is_running()) { 23 string exec{string_util::replace_all(m_exec, "%counter%", to_string(++m_counter))}; 24 m_log.info("%s: Invoking shell command: \"%s\"", name(), exec); 25 m_command = command_util::make_command<output_policy::REDIRECTED>(exec); 26 27 try { 28 m_command->exec(false); 29 } catch (const exception& err) { 30 m_log.err("%s: %s", name(), err.what()); 31 throw module_error("Failed to execute command, stopping module..."); 32 } 33 } 34 35 int fd = m_command->get_stdout(PIPE_READ); 36 while (!m_stopping && fd != -1 && m_command->is_running() && !io_util::poll(fd, POLLHUP, 0)) { 37 if (!io_util::poll_read(fd, 25)) { 38 continue; 39 } else if ((m_output = m_command->readline()) != m_prev) { 40 m_prev = m_output; 41 broadcast(); 42 } 43 } 44 45 if (m_stopping) { 46 return chrono::duration<double>{0}; 47 } else if (m_command && !m_command->is_running()) { 48 return std::max(m_command->get_exit_status() == 0 ? m_interval : 1s, m_interval); 49 } else { 50 return m_interval; 51 } 52 }; 53 } 54 55 // }}} 56 // Handler for basic shell commands {{{ 57 58 return [&] { 59 try { 60 auto exec = string_util::replace_all(m_exec, "%counter%", to_string(++m_counter)); 61 m_log.info("%s: Invoking shell command: \"%s\"", name(), exec); 62 m_command = command_util::make_command<output_policy::REDIRECTED>(exec); 63 m_command->exec(true); 64 } catch (const exception& err) { 65 m_log.err("%s: %s", name(), err.what()); 66 throw module_error("Failed to execute command, stopping module..."); 67 } 68 69 int fd = m_command->get_stdout(PIPE_READ); 70 if (fd != -1 && io_util::poll_read(fd) && (m_output = m_command->readline()) != m_prev) { 71 broadcast(); 72 m_prev = m_output; 73 } else if (m_command->get_exit_status() != 0) { 74 m_output.clear(); 75 m_prev.clear(); 76 broadcast(); 77 } 78 79 return std::max(m_command->get_exit_status() == 0 ? m_interval : 1s, m_interval); 80 }; 81 82 // }}} 83 }()) { 84 // Load configuration values 85 m_exec = m_conf.get(name(), "exec", m_exec); 86 m_exec_if = m_conf.get(name(), "exec-if", m_exec_if); 87 m_interval = m_conf.get<decltype(m_interval)>(name(), "interval", 5s); 88 89 // Load configured click handlers 90 m_actions[mousebtn::LEFT] = m_conf.get(name(), "click-left", ""s); 91 m_actions[mousebtn::MIDDLE] = m_conf.get(name(), "click-middle", ""s); 92 m_actions[mousebtn::RIGHT] = m_conf.get(name(), "click-right", ""s); 93 m_actions[mousebtn::DOUBLE_LEFT] = m_conf.get(name(), "double-click-left", ""s); 94 m_actions[mousebtn::DOUBLE_MIDDLE] = m_conf.get(name(), "double-click-middle", ""s); 95 m_actions[mousebtn::DOUBLE_RIGHT] = m_conf.get(name(), "double-click-right", ""s); 96 m_actions[mousebtn::SCROLL_UP] = m_conf.get(name(), "scroll-up", ""s); 97 m_actions[mousebtn::SCROLL_DOWN] = m_conf.get(name(), "scroll-down", ""s); 98 99 // Setup formatting 100 m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL}); 101 if (m_formatter->has(TAG_LABEL)) { 102 m_label = load_optional_label(m_conf, name(), "label", "%output%"); 103 } 104 } 105 106 /** 107 * Start the module worker 108 */ start()109 void script_module::start() { 110 m_mainthread = thread([&] { 111 try { 112 while (running() && !m_stopping) { 113 if (check_condition()) { 114 sleep(process(m_handler)); 115 } else if (m_interval > 1s) { 116 sleep(m_interval); 117 } else { 118 sleep(1s); 119 } 120 } 121 } catch (const exception& err) { 122 halt(err.what()); 123 } 124 }); 125 } 126 127 /** 128 * Stop the module worker by terminating any running commands 129 */ stop()130 void script_module::stop() { 131 m_stopping = true; 132 wakeup(); 133 134 std::lock_guard<decltype(m_handler)> guard(m_handler); 135 136 m_command.reset(); 137 module::stop(); 138 } 139 140 /** 141 * Check if defined condition is met 142 */ check_condition()143 bool script_module::check_condition() { 144 if (m_exec_if.empty()) { 145 return true; 146 } else if (command_util::make_command<output_policy::IGNORED>(m_exec_if)->exec(true) == 0) { 147 return true; 148 } else if (!m_output.empty()) { 149 broadcast(); 150 m_output.clear(); 151 m_prev.clear(); 152 } 153 return false; 154 } 155 156 /** 157 * Process mutex wrapped script handler 158 */ process(const decltype(m_handler)& handler) const159 chrono::duration<double> script_module::process(const decltype(m_handler) & handler) const { 160 std::lock_guard<decltype(handler)> guard(handler); 161 return handler(); 162 } 163 164 /** 165 * Generate module output 166 */ get_output()167 string script_module::get_output() { 168 if (m_output.empty()) { 169 return ""; 170 } 171 172 if (m_label) { 173 m_label->reset_tokens(); 174 m_label->replace_token("%output%", m_output); 175 } 176 177 string cnt{to_string(m_counter)}; 178 string output{module::get_output()}; 179 180 for (auto btn : {mousebtn::LEFT, mousebtn::MIDDLE, mousebtn::RIGHT, 181 mousebtn::DOUBLE_LEFT, mousebtn::DOUBLE_MIDDLE, 182 mousebtn::DOUBLE_RIGHT, mousebtn::SCROLL_UP, 183 mousebtn::SCROLL_DOWN}) { 184 185 auto action = m_actions[btn]; 186 187 if (!action.empty()) { 188 auto action_replaced = string_util::replace_all(action, "%counter%", cnt); 189 190 /* 191 * The pid token is only for tailed commands. 192 * If the command is not specified or running, replacement is unnecessary as well 193 */ 194 if(m_tail && m_command && m_command->is_running()) { 195 action_replaced = string_util::replace_all(action_replaced, "%pid%", to_string(m_command->get_pid())); 196 } 197 m_builder->action(btn, action_replaced); 198 } 199 } 200 201 m_builder->append(output); 202 203 return m_builder->flush(); 204 } 205 206 /** 207 * Output format tags 208 */ build(builder * builder,const string & tag) const209 bool script_module::build(builder* builder, const string& tag) const { 210 if (tag == TAG_LABEL) { 211 builder->node(m_label); 212 } else { 213 return false; 214 } 215 216 return true; 217 } 218 } 219 220 POLYBAR_NS_END 221