mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-20 03:35:41 +02:00
module/script: add poll-interval option
When set to a non-negative value, the script module will call the configured script every <poll-interval> second. In this mode, the script is expected to write one tag set and then exit. This is intended to simplify the implementation of scripts that would otherwise just do a loop + sleep. Closes #67
This commit is contained in:
parent
e4a0b375e5
commit
cf41d008f8
3 changed files with 101 additions and 17 deletions
|
@ -13,6 +13,8 @@
|
||||||
* Text shaping support.
|
* Text shaping support.
|
||||||
* Support for middle and right mouse buttons, mouse wheel and trackpad
|
* Support for middle and right mouse buttons, mouse wheel and trackpad
|
||||||
scrolling (https://codeberg.org/dnkl/yambar/issues/39).
|
scrolling (https://codeberg.org/dnkl/yambar/issues/39).
|
||||||
|
* script: polling mode. See the new `poll-interval` option
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/67).
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -8,11 +8,16 @@ script - This module executes a user-provided script (or binary!)
|
||||||
This module executes a user-provided script (or binary!) that writes
|
This module executes a user-provided script (or binary!) that writes
|
||||||
tags on its stdout.
|
tags on its stdout.
|
||||||
|
|
||||||
The script can either exit immediately after writing a set of tags, in
|
Scripts can be run in two modes: yambar polled, or continously. In the
|
||||||
which case yambar will display those tags until yambar is
|
yambar polled mode, the script is expected to write one set of tags
|
||||||
terminated. Or, the script can continue executing and update yambar
|
and then exit. Yambar will execute the script again after a
|
||||||
with new tag sets, either periodically, or when there is new data to
|
configurable amount of time.
|
||||||
feed to yambar.
|
|
||||||
|
In continous mode, the script is executed once. It will typically run
|
||||||
|
in a loop, sending an updated tag set whenever it needs, or wants
|
||||||
|
to. The last tag set is used (displayed) by yambar until a new tag set
|
||||||
|
is received. This mode is intended to be used by scripts that depends
|
||||||
|
on non-polling methods to update their state.
|
||||||
|
|
||||||
Tag sets, or _transactions_, are separated by an empty line
|
Tag sets, or _transactions_, are separated by an empty line
|
||||||
(e.g. *echo ""*). The empty line is required to commit (update) the
|
(e.g. *echo ""*). The empty line is required to commit (update) the
|
||||||
|
@ -70,6 +75,10 @@ User defined.
|
||||||
: list of strings
|
: list of strings
|
||||||
: no
|
: no
|
||||||
: Arguments to pass to the script/binary.
|
: Arguments to pass to the script/binary.
|
||||||
|
| poll-interval
|
||||||
|
: integer
|
||||||
|
: Number of seconds between each script run. If unset, continous mode
|
||||||
|
is used.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
@ -89,8 +98,9 @@ while true; do
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
This script will emit a single string tag, _test_, and alternate its
|
This script runs in continous mode, and will emit a single string tag,
|
||||||
value between *hello* and *world* every three seconds.
|
_test_, and alternate its value between *hello* and *world* every
|
||||||
|
three seconds.
|
||||||
|
|
||||||
A corresponding yambar configuration could look like this:
|
A corresponding yambar configuration could look like this:
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ struct private {
|
||||||
char *path;
|
char *path;
|
||||||
size_t argc;
|
size_t argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
|
int poll_interval;
|
||||||
|
bool aborted;
|
||||||
|
|
||||||
struct particle *content;
|
struct particle *content;
|
||||||
|
|
||||||
|
@ -331,7 +333,7 @@ data_received(struct module *mod, const char *data, size_t len)
|
||||||
static int
|
static int
|
||||||
run_loop(struct module *mod, pid_t pid, int comm_fd)
|
run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
struct pollfd fds[] = {
|
struct pollfd fds[] = {
|
||||||
|
@ -360,19 +362,18 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||||
data_received(mod, data, amount);
|
data_received(mod, data, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[0].revents & POLLHUP) {
|
if (fds[0].revents & (POLLHUP | POLLIN)) {
|
||||||
/* Aborted */
|
/* Aborted */
|
||||||
|
struct private *m = mod->private;
|
||||||
|
m->aborted = true;
|
||||||
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[1].revents & POLLHUP) {
|
if (fds[1].revents & POLLHUP) {
|
||||||
/* Child's stdout closed */
|
/* Child's stdout closed */
|
||||||
LOG_DBG("script pipe closed (script terminated?)");
|
LOG_DBG("script pipe closed (script terminated?)");
|
||||||
break;
|
ret = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (fds[0].revents & POLLIN) {
|
|
||||||
/* Aborted */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,7 +382,7 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
run(struct module *mod)
|
execute_script(struct module *mod)
|
||||||
{
|
{
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
|
|
||||||
|
@ -565,9 +566,75 @@ run(struct module *mod)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
run(struct module *mod)
|
||||||
|
{
|
||||||
|
struct private *m = mod->private;
|
||||||
|
|
||||||
|
int ret = 1;
|
||||||
|
bool keep_going = true;
|
||||||
|
|
||||||
|
while (keep_going && !m->aborted) {
|
||||||
|
ret = execute_script(mod);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
if (m->aborted)
|
||||||
|
break;
|
||||||
|
if (m->poll_interval < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
struct timeval now;
|
||||||
|
if (gettimeofday(&now, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to get current time");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval poll_interval = {.tv_sec = m->poll_interval};
|
||||||
|
|
||||||
|
struct timeval timeout;
|
||||||
|
timeradd(&now, &poll_interval, &timeout);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
||||||
|
|
||||||
|
struct timeval now;
|
||||||
|
if (gettimeofday(&now, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to get current time");
|
||||||
|
keep_going = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timercmp(&now, &timeout, <)) {
|
||||||
|
/* We’ve reached the timeout, it’s time to execute the script again */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval time_left;
|
||||||
|
timersub(&timeout, &now, &time_left);
|
||||||
|
|
||||||
|
int r = poll(fds, 1, time_left.tv_sec * 1000 + time_left.tv_usec / 1000);
|
||||||
|
if (r < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
LOG_ERRNO("failed to poll");
|
||||||
|
keep_going = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r > 0) {
|
||||||
|
m->aborted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static struct module *
|
static struct module *
|
||||||
script_new(const char *path, size_t argc, const char *const argv[static argc],
|
script_new(const char *path, size_t argc, const char *const argv[static argc],
|
||||||
struct particle *_content)
|
int poll_interval, struct particle *_content)
|
||||||
{
|
{
|
||||||
struct private *m = calloc(1, sizeof(*m));
|
struct private *m = calloc(1, sizeof(*m));
|
||||||
m->path = strdup(path);
|
m->path = strdup(path);
|
||||||
|
@ -576,6 +643,7 @@ script_new(const char *path, size_t argc, const char *const argv[static argc],
|
||||||
m->argv = malloc(argc * sizeof(m->argv[0]));
|
m->argv = malloc(argc * sizeof(m->argv[0]));
|
||||||
for (size_t i = 0; i < argc; i++)
|
for (size_t i = 0; i < argc; i++)
|
||||||
m->argv[i] = strdup(argv[i]);
|
m->argv[i] = strdup(argv[i]);
|
||||||
|
m->poll_interval = poll_interval;
|
||||||
|
|
||||||
struct module *mod = module_common_new();
|
struct module *mod = module_common_new();
|
||||||
mod->private = m;
|
mod->private = m;
|
||||||
|
@ -592,6 +660,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
const struct yml_node *path = yml_get_value(node, "path");
|
const struct yml_node *path = yml_get_value(node, "path");
|
||||||
const struct yml_node *args = yml_get_value(node, "args");
|
const struct yml_node *args = yml_get_value(node, "args");
|
||||||
const struct yml_node *c = yml_get_value(node, "content");
|
const struct yml_node *c = yml_get_value(node, "content");
|
||||||
|
const struct yml_node *poll_interval = yml_get_value(node, "poll-interval");
|
||||||
|
|
||||||
size_t argc = args != NULL ? yml_list_length(args) : 0;
|
size_t argc = args != NULL ? yml_list_length(args) : 0;
|
||||||
const char *argv[argc];
|
const char *argv[argc];
|
||||||
|
@ -607,7 +676,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
}
|
}
|
||||||
|
|
||||||
return script_new(
|
return script_new(
|
||||||
yml_value_as_string(path), argc, argv, conf_to_particle(c, inherited));
|
yml_value_as_string(path), argc, argv,
|
||||||
|
poll_interval != NULL ? yml_value_as_int(poll_interval) : -1,
|
||||||
|
conf_to_particle(c, inherited));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -637,6 +708,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node)
|
||||||
static const struct attr_info attrs[] = {
|
static const struct attr_info attrs[] = {
|
||||||
{"path", true, &conf_verify_path},
|
{"path", true, &conf_verify_path},
|
||||||
{"args", false, &conf_verify_args},
|
{"args", false, &conf_verify_args},
|
||||||
|
{"poll-interval", false, &conf_verify_int},
|
||||||
MODULE_COMMON_ATTRS,
|
MODULE_COMMON_ATTRS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue