feat(ipc): hook, prev, next, reset module actions (#2528)
* WIP ipc actions * feat(ipc): Add hook, prev, next and reset actions Closes: #2464 * ipc: format code * ipc: fix comparison * Apply suggestions from code review Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> * ipc: make index 0-based * ipc: add 0-based indexing breaking change to Changelog * ipc: restore 1-based index for and message * ipc: fix initial=0 throwing an error Co-authored-by: Martin Terneborg <martinterneborg@protonmail.com> Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com>
This commit is contained in:
parent
22014c70c4
commit
231af35354
@ -132,6 +132,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
([`#1441`](https://github.com/polybar/polybar/issues/1441))
|
([`#1441`](https://github.com/polybar/polybar/issues/1441))
|
||||||
- `internal/xkeyboard`: Allow configuring icons using variant
|
- `internal/xkeyboard`: Allow configuring icons using variant
|
||||||
([`#2414`](https://github.com/polybar/polybar/issues/2414))
|
([`#2414`](https://github.com/polybar/polybar/issues/2414))
|
||||||
|
- `custom/ipc`: Add `hook`, `next`, `prev`, `reset` actions to the ipc module
|
||||||
|
([`#2464`](https://github.com/polybar/polybar/issues/2464))
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Polybar now also reads `config.ini` when searching for config files.
|
- Polybar now also reads `config.ini` when searching for config files.
|
||||||
|
@ -261,10 +261,17 @@ custom/menu
|
|||||||
custom/ipc
|
custom/ipc
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
:``send``: *(Has Data)* Replace the contents of the module with the data passed in this action.
|
|
||||||
|
|
||||||
.. versionadded:: 3.6.0
|
.. versionadded:: 3.6.0
|
||||||
|
|
||||||
|
:``send``: *(Has Data)* Replace the contents of the module with the data passed in this action.
|
||||||
|
:``hook``: *(Has Data)* Trigger the given hook.
|
||||||
|
|
||||||
|
The data is the 0-based index of the hook to trigger.
|
||||||
|
:``next``: Switches to the next hook and wrap around when the last hook was displayed.
|
||||||
|
:``prev``: Switches to the previous hook and wrap around when the first hook was displayed.
|
||||||
|
:``reset``: Reset the module to its startup state: either empty or according to the ``initial`` setting.
|
||||||
|
|
||||||
|
|
||||||
Deprecated Action Names
|
Deprecated Action Names
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
@ -35,9 +35,17 @@ namespace modules {
|
|||||||
static constexpr auto TYPE = "custom/ipc";
|
static constexpr auto TYPE = "custom/ipc";
|
||||||
|
|
||||||
static constexpr auto EVENT_SEND = "send";
|
static constexpr auto EVENT_SEND = "send";
|
||||||
|
static constexpr auto EVENT_HOOK = "hook";
|
||||||
|
static constexpr auto EVENT_NEXT = "next";
|
||||||
|
static constexpr auto EVENT_PREV = "prev";
|
||||||
|
static constexpr auto EVENT_RESET = "reset";
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void action_send(const string& data);
|
void action_send(const string& data);
|
||||||
|
void action_hook(const string& data);
|
||||||
|
void action_next();
|
||||||
|
void action_prev();
|
||||||
|
void action_reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr const char* TAG_OUTPUT{"<output>"};
|
static constexpr const char* TAG_OUTPUT{"<output>"};
|
||||||
@ -45,6 +53,8 @@ namespace modules {
|
|||||||
map<mousebtn, string> m_actions;
|
map<mousebtn, string> m_actions;
|
||||||
string m_output;
|
string m_output;
|
||||||
size_t m_initial;
|
size_t m_initial;
|
||||||
|
size_t m_current_hook;
|
||||||
|
void exec_hook();
|
||||||
};
|
};
|
||||||
} // namespace modules
|
} // namespace modules
|
||||||
|
|
||||||
|
@ -14,8 +14,12 @@ namespace modules {
|
|||||||
* Load user-defined ipc hooks and
|
* Load user-defined ipc hooks and
|
||||||
* create formatting tags
|
* create formatting tags
|
||||||
*/
|
*/
|
||||||
ipc_module::ipc_module(const bar_settings& bar, string name_) : module<ipc_module>(bar, move(name_)) {
|
ipc_module::ipc_module(const bar_settings& bar, string name_) : module<ipc_module>(bar, move(name_)), m_initial(0) {
|
||||||
m_router->register_action_with_data(EVENT_SEND, [this](const std::string& data) { action_send(data); });
|
m_router->register_action_with_data(EVENT_SEND, [this](const std::string& data) { action_send(data); });
|
||||||
|
m_router->register_action_with_data(EVENT_HOOK, [this](const std::string& data) { action_hook(data); });
|
||||||
|
m_router->register_action(EVENT_NEXT, [this]() { action_next(); });
|
||||||
|
m_router->register_action(EVENT_PREV, [this]() { action_prev(); });
|
||||||
|
m_router->register_action(EVENT_RESET, [this]() { action_reset(); });
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
|
||||||
@ -25,8 +29,16 @@ namespace modules {
|
|||||||
|
|
||||||
m_log.info("%s: Loaded %d hooks", name(), m_hooks.size());
|
m_log.info("%s: Loaded %d hooks", name(), m_hooks.size());
|
||||||
|
|
||||||
if ((m_initial = m_conf.get(name(), "initial", 0_z)) && m_initial > m_hooks.size()) {
|
m_initial = m_conf.get(name(), "initial", 0_z);
|
||||||
throw module_error("Initial hook out of bounds (defined: " + to_string(m_hooks.size()) + ")");
|
if (m_conf.has(name(), "initial") && m_initial != 0) {
|
||||||
|
if (m_initial <= m_hooks.size()) {
|
||||||
|
m_current_hook = m_initial - 1;
|
||||||
|
} else {
|
||||||
|
throw module_error("Initial hook out of bounds '" + to_string(m_initial) + "'. Defined hooks goes from 1 to " +
|
||||||
|
to_string(m_hooks.size()) + ")");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_current_hook = m_hooks.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -62,11 +74,9 @@ namespace modules {
|
|||||||
broadcast();
|
broadcast();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_initial) {
|
if (m_initial != 0) {
|
||||||
// TODO do this in a thread.
|
// TODO do this in a thread.
|
||||||
auto command = command_util::make_command<output_policy::REDIRECTED>(m_hooks.at(m_initial - 1)->command);
|
exec_hook();
|
||||||
command->exec(false);
|
|
||||||
command->tail([this](string line) { m_output = line; });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,25 +117,14 @@ namespace modules {
|
|||||||
* execute its command
|
* execute its command
|
||||||
*/
|
*/
|
||||||
void ipc_module::on_message(const string& message) {
|
void ipc_module::on_message(const string& message) {
|
||||||
for (auto&& hook : m_hooks) {
|
for (size_t i = 0; i < m_hooks.size(); i++) {
|
||||||
if (hook->payload != message) {
|
const auto& hook = m_hooks[i];
|
||||||
continue;
|
if (hook->payload == message) {
|
||||||
}
|
|
||||||
|
|
||||||
m_log.info("%s: Found matching hook (%s)", name(), hook->payload);
|
m_log.info("%s: Found matching hook (%s)", name(), hook->payload);
|
||||||
|
m_current_hook = i;
|
||||||
try {
|
this->exec_hook();
|
||||||
// Clear the output in case the command produces no output
|
break;
|
||||||
m_output.clear();
|
|
||||||
auto command = command_util::make_command<output_policy::REDIRECTED>(hook->command);
|
|
||||||
command->exec(false);
|
|
||||||
command->tail([this](string line) { m_output = line; });
|
|
||||||
} catch (const exception& err) {
|
|
||||||
m_log.err("%s: Failed to execute hook command (err: %s)", err.what());
|
|
||||||
m_output.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +132,69 @@ namespace modules {
|
|||||||
m_output = data;
|
m_output = data;
|
||||||
broadcast();
|
broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ipc_module::action_hook(const string& data) {
|
||||||
|
try {
|
||||||
|
int hook = std::stoi(data);
|
||||||
|
|
||||||
|
if (hook < 0 || (size_t)hook >= m_hooks.size()) {
|
||||||
|
m_log.err("%s: Hook action received with an out of bounds hook '%s'. Defined hooks goes from 0 to %zu.", name(),
|
||||||
|
data, m_hooks.size() - 1);
|
||||||
|
} else {
|
||||||
|
m_current_hook = hook;
|
||||||
|
this->exec_hook();
|
||||||
|
}
|
||||||
|
} catch (const std::invalid_argument& err) {
|
||||||
|
m_log.err(
|
||||||
|
"%s: Hook action received '%s' cannot be converted to a valid hook index. Defined hooks goes from 0 to %zu.",
|
||||||
|
name(), data, m_hooks.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_module::action_next() {
|
||||||
|
// this is the case where initial is not defined on first 'next'
|
||||||
|
if (m_current_hook == m_hooks.size()) {
|
||||||
|
m_current_hook = 0;
|
||||||
|
} else {
|
||||||
|
m_current_hook = (m_current_hook + 1) % m_hooks.size();
|
||||||
|
}
|
||||||
|
this->exec_hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_module::action_prev() {
|
||||||
|
if (m_current_hook == 0) {
|
||||||
|
m_current_hook = m_hooks.size();
|
||||||
|
}
|
||||||
|
m_current_hook--;
|
||||||
|
this->exec_hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_module::action_reset() {
|
||||||
|
if (m_initial != 0) {
|
||||||
|
m_current_hook = m_initial - 1;
|
||||||
|
this->exec_hook();
|
||||||
|
} else {
|
||||||
|
m_current_hook = m_hooks.size();
|
||||||
|
m_output.clear();
|
||||||
|
broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_module::exec_hook() {
|
||||||
|
// Clear the output in case the command produces no output
|
||||||
|
m_output.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto command = command_util::make_command<output_policy::REDIRECTED>(m_hooks[m_current_hook]->command);
|
||||||
|
command->exec(false);
|
||||||
|
command->tail([this](string line) { m_output = line; });
|
||||||
|
} catch (const exception& err) {
|
||||||
|
m_log.err("%s: Failed to execute hook command (err: %s)", name(), err.what());
|
||||||
|
m_output.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast();
|
||||||
|
}
|
||||||
} // namespace modules
|
} // namespace modules
|
||||||
|
|
||||||
POLYBAR_NS_END
|
POLYBAR_NS_END
|
||||||
|
Loading…
Reference in New Issue
Block a user