aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.c34
-rw-r--r--src/screen.c29
-rw-r--r--src/vega.h10
-rw-r--r--src/web.c17
4 files changed, 82 insertions, 8 deletions
diff --git a/src/main.c b/src/main.c
index ebda312..4a313e9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -25,12 +25,13 @@
* written again replaces the running one and states keep their values by
* name: aup watch app.aup -o app.pmod beside it makes a saved edit show at once.
* Script commands, one per line: key KEYNAME | text HEX|- | type HEX | enter |
- * tab | frame [N] | point VIEW ROW | click LINE COLUMN | swap MODULE | show. key presses and
+ * tab | frame [N] | point VIEW ROW | click LINE COLUMN | time MS | tick TIMER | swap MODULE | show. key presses and
* releases the input bound to that key; text commits text to the screen's text
* input; type, enter and tab act on the TUI's input line; point focuses a row
* (0 first) of a list or table; click is a mouse click on the last shown TUI
- * frame (1 first, as terminals report it); swap replaces the module as --watch
- * does. */
+ * frame (1 first, as terminals report it); time sets the time the next
+ * events see (live surfaces use the clock) and tick ticks a timer (live
+ * surfaces tick them on time); swap replaces the module as --watch does. */
static PureModule modules[2]; /* the running one (app.module) and the next */
static unsigned current;
@@ -405,6 +406,8 @@ static int interactive(void) {
signal(SIGWINCH, on_resize);
VegaGrid grid = {0};
drawn = &grid;
+ VegaTimers timers;
+ vega_timers_start(&timers, app.module, vega_now());
for (;;) {
if (resized) {
struct winsize size;
@@ -413,13 +416,19 @@ static int interactive(void) {
vega_grid(&grid, size.ws_col, size.ws_row);
else vega_grid(&grid, 80, 24);
}
+ /* Tick what is due, draw, then wait for input, a frame or the next tick. */
+ bool ticked = false;
+ long due = vega_timers_run(&timers, &app, vega_now(), &ticked);
vega_draw(&grid, &app, &screen, entry, entry_length, typing);
paint_terminal(&grid);
struct pollfd wait = {STDIN_FILENO, POLLIN, 0};
- int ready = poll(&wait, 1, screen.ticks ? 16 : watched ? 200 : -1);
+ int timeout = screen.ticks ? 16 : watched ? 200 : -1;
+ if (due >= 0 && (timeout < 0 || due < timeout)) timeout = (int)due;
+ int ready = poll(&wait, 1, timeout);
+ app.now = vega_now();
if (ready == 0) {
if (screen.ticks) pure_app_frame(&app);
- module_changed();
+ if (module_changed()) vega_timers_start(&timers, app.module, app.now);
continue;
}
if (ready < 0) continue; /* a signal, such as a resize */
@@ -480,6 +489,21 @@ static int script(Surface surface, unsigned width, unsigned height, bool paintin
if (surface != SURFACE_TUI) { fputs("vega: click needs the tui\n", stderr); continue; }
acted = sscanf(line, "%*s %u %u", &at_line, &at_column) == 2 && at_line && shown;
ok = acted && mouse(0, at_line - 1);
+ } else if (!strcmp(command, "time")) {
+ /* The time the next events see: scripts replay any. */
+ char *end;
+ double now = strtod(argument, &end);
+ acted = false;
+ if (!*argument || *end || !(now >= 0 && now < 1e15)) fprintf(stderr, "vega: time takes milliseconds\n");
+ else app.now = now;
+ } else if (!strcmp(command, "tick")) {
+ input = pure_module_input(app.module, argument);
+ verb = "tick";
+ if (input < 0 || app.module->input[input].kind != PURE_KIND_TIMER) {
+ fprintf(stderr, "vega: no timer %s\n", argument);
+ input = -1;
+ ok = false;
+ } else ok = pure_app_tick(&app, (uint32_t)input) == PURE_OK;
} else if (!strcmp(command, "swap")) {
acted = false;
if (!swap(argument, read_module(argument))) fprintf(stderr, "vega: kept the running module\n");
diff --git a/src/screen.c b/src/screen.c
index 56c2cf9..fdecaf4 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1,7 +1,36 @@
+#define _POSIX_C_SOURCE 200809L
#include "vega.h"
#include "module-text.h"
#include <math.h>
#include <string.h>
+#include <time.h>
+
+double vega_now(void) {
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+ return floor((double)t.tv_sec * 1000 + (double)t.tv_nsec / 1e6);
+}
+
+void vega_timers_start(VegaTimers *timers, const PureModule *m, double now) {
+ for (uint32_t i = 0; i < m->inputs; ++i)
+ timers->next[i] = m->input[i].kind == PURE_KIND_TIMER ? now + m->input[i].period : 0;
+}
+
+long vega_timers_run(VegaTimers *timers, PureApp *app, double now, bool *ticked) {
+ const PureModule *m = app->module;
+ double soonest = -1;
+ for (uint32_t i = 0; i < m->inputs; ++i) {
+ if (m->input[i].kind != PURE_KIND_TIMER) continue;
+ if (timers->next[i] <= now) {
+ app->now = now;
+ pure_app_tick(app, i); /* a failed tick changes nothing, as any failed event */
+ *ticked = true;
+ timers->next[i] = now + m->input[i].period;
+ }
+ if (soonest < 0 || timers->next[i] - now < soonest) soonest = timers->next[i] - now;
+ }
+ return soonest < 0 ? -1 : (long)ceil(soonest);
+}
static unsigned long evaluations;
diff --git a/src/vega.h b/src/vega.h
index 47e7e91..f01667b 100644
--- a/src/vega.h
+++ b/src/vega.h
@@ -36,6 +36,16 @@ typedef struct {
} VegaScreen;
void vega_screen(VegaScreen *, const PureModule *);
+
+/* The wall-clock time events see on a live surface: milliseconds since 1970
+ * (UTC). Scripts set their own (time MS). */
+double vega_now(void);
+/* When each timer input ticks next. run ticks every one due at now (a tick
+ * sees now) and gives the milliseconds until the next, or -1 without timers;
+ * ticked says whether any did. Missed ticks are not made up. */
+typedef struct { double next[PURE_MODULE_INPUTS]; } VegaTimers;
+void vega_timers_start(VegaTimers *, const PureModule *, double now);
+long vega_timers_run(VegaTimers *, PureApp *, double now, bool *ticked);
/* What a module seems to mean but does not meet: a list label that is not
* text, a _focus view without its table, a column a list never shows, a
* second text input. Each finding names the view or input it is about;
diff --git a/src/web.c b/src/web.c
index 8202815..535d92f 100644
--- a/src/web.c
+++ b/src/web.c
@@ -382,7 +382,12 @@ int vega_web(PureApp *app, const VegaScreen *screen, const char *listen_on, unsi
signal(SIGPIPE, SIG_IGN);
static Client clients[CLIENTS];
for (unsigned i = 0; i < CLIENTS; ++i) clients[i].fd = -1;
+ VegaTimers timers;
+ vega_timers_start(&timers, app->module, vega_now());
while (!stopping) {
+ /* Tick what is due; every page hears of it this round, without waiting. */
+ bool ticked = false;
+ long due = vega_timers_run(&timers, app, vega_now(), &ticked);
struct pollfd fds[CLIENTS + 1];
unsigned slot[CLIENTS + 1], count = 1;
fds[0] = (struct pollfd){listener, POLLIN, 0};
@@ -392,11 +397,17 @@ int vega_web(PureApp *app, const VegaScreen *screen, const char *listen_on, unsi
fds[count] = (struct pollfd){c->fd, (short)(POLLIN | (c->out_at < c->out.used ? POLLOUT : 0)), 0};
slot[count++] = i;
}
- int ready = poll(fds, count, screen->ticks ? 33 : reload ? 200 : 1000);
+ int timeout = ticked ? 0 : screen->ticks ? 33 : reload ? 200 : 1000;
+ if (due >= 0 && due < timeout) timeout = (int)due;
+ int ready = poll(fds, count, timeout);
if (ready < 0 && errno != EINTR) break;
- bool changed = false;
+ bool changed = ticked;
+ app->now = vega_now(); /* what the events of this round see */
if (ready == 0 && screen->ticks) changed = pure_app_frame(app) == PURE_OK;
- if (reload && reload()) changed = true; /* a new module: every page draws it */
+ if (reload && reload()) { /* a new module: every page draws it */
+ changed = true;
+ vega_timers_start(&timers, app->module, app->now);
+ }
if (fds[0].revents & POLLIN)
for (int fd; (fd = accept4(listener, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC)) >= 0;) {
unsigned free_slot = CLIENTS;