diff options
| author | eunhhu <[email protected]> | 2026-09-27 19:37:33 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-27 19:37:33 +0100 |
| commit | 5fe8104744d3e0a48674b1368db6fd104a10bdd4 (patch) | |
| tree | ed9ea83c6a84a09136f40dc7ff6141968e75faec /surfaces/main.c | |
| parent | ad3eb77af098f34c1ea8cb50f468a8b75fb7d61c (diff) | |
| download | vega-5fe8104744d3e0a48674b1368db6fd104a10bdd4.tar.gz vega-5fe8104744d3e0a48674b1368db6fd104a10bdd4.zip | |
Show a Pure node's app on its display, as a surface in the node
The vega host builds a second way, as build/vega-surface.so, which
pure-serve maps and runs beside its connection threads (pure-serve
--surface vega-surface.so -- display ..., Pure's design 010). There the
app it draws is a reader of the node's last save, read where it is,
and what people type goes to the node's owner: a key press as the key
input's event, the typed line as the text input's, each applied and
saved with the node's requests. It waits on its input devices and the
node's changes descriptor together, leaves timers and signals to the
node, and stops with it. The library calls the app model pure-serve
exports and links none of its own; it exports pure_surface_run alone.
Without --live or --input it takes no input: the node's saves alone draw
it.
The check gives a node an item over HTTP, then runs the display in the
node with recorded keys (an item, Enter, Tab, Space): the last frame is
the one vega tui draws for the same state and keys, it came back from
the display controller as drawn, and the node's views show both items.
Diffstat (limited to 'surfaces/main.c')
| -rw-r--r-- | surfaces/main.c | 125 |
1 files changed, 113 insertions, 12 deletions
diff --git a/surfaces/main.c b/surfaces/main.c index 26aa4eb..a7488ab 100644 --- a/surfaces/main.c +++ b/surfaces/main.c @@ -2,6 +2,7 @@ #include "vega.h" #include "module-store.h" #include "store.h" +#include "surface.h" #include <poll.h> #include <signal.h> #include <stdlib.h> @@ -42,11 +43,19 @@ * (0 first) of a list or table; click is a mouse click on the last shown TUI * 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. */ + * surfaces tick them on time); swap replaces the module as --watch does. + * + * Built with VEGA_SURFACE the same host is a library a Pure node maps + * (pure-serve --surface vega-surface.so -- display ARGUMENTS, Pure's + * serve/surface.h): the display shows the node's app as of each save, read + * where it is, and what people type goes to the node's owner. */ static PureModule modules[2]; /* the running one (app.module) and the next */ static unsigned current; static PureApp app; +/* In a node: the node, whose owner applies the events; app then shows its + * last save (a reader) and applies none itself. */ +static const PureSurface *node; static VegaScreen screen; static VegaGrid *drawn; /* the TUI's last frame, which a click lands on */ static char entry[VEGA_TEXT]; @@ -153,10 +162,16 @@ static int key_input(const char *key) { return vega_key_input(app.module, key); /* A key press and release; false when the app has no such key or the press failed. */ static bool press(const char *key, int *input) { *input = key_input(key); + if (*input >= 0 && node) return node->press(node->host, (uint32_t)*input) == PURE_OK; return *input >= 0 && vega_press(&app, key); } -static bool commit(const char *text, size_t length) { return vega_commit(&app, &screen, text, length); } +static bool commit(const char *text, size_t length) { + if (node) + return screen.prompt >= 0 && + node->type(node->host, (uint32_t)screen.prompt, (PureSpan){(const unsigned char *)text, length}) == PURE_OK; + return vega_commit(&app, &screen, text, length); +} static const VegaPart *part_named(const char *name) { for (unsigned i = 0; i < screen.count; ++i) @@ -179,7 +194,7 @@ static bool mouse(unsigned button, unsigned line) { item = button == 64 ? focus - 1 : focus + 1; } } else if (button) return false; - return vega_point(&app, part, item); + return !node && vega_point(&app, part, item); /* a node takes keys and text, not pointing yet */ } static int hex(char c) { return c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1; } @@ -552,23 +567,29 @@ static void show_pointer(uint32_t *out, size_t stride, int x, int y, unsigned wi } /* Draw, show, then wait for input or the next tick, until a signal (or the - * end of recorded events: the last frame is then printed as text). */ + * end of recorded events: the last frame is then printed as text). In a + * node: draw its last save, wait for input or its next save, until it stops; + * its owner runs the timers, and the signals are its own. */ static int live(VegaInput *input, unsigned columns, unsigned rows, bool recorded) { VegaGrid grid = {0}; if (!vega_grid(&grid, columns, rows)) return 2; drawn = &grid; - signal(SIGINT, on_stop); - signal(SIGTERM, on_stop); + if (!node) { + signal(SIGINT, on_stop); + signal(SIGTERM, on_stop); + } VegaTimers timers; vega_timers_start(&timers, app.module, vega_now()); char path[512] = ""; unsigned long differing = 0; bool open_input = true; int status = 0; - while (!stopping) { + while (!stopping && !(node && node->stopping(node->host))) { bool ticked = false; - long due = vega_timers_run(&timers, &app, vega_now(), &ticked); - vega_draw(&grid, &app, &screen, entry, entry_length, typing); + long due = node ? -1 : vega_timers_run(&timers, &app, vega_now(), &ticked); + PureSnapshot *snapshot = node ? node->read(node->host, &app) : NULL; + if (!node || snapshot) vega_draw(&grid, &app, &screen, entry, entry_length, typing); + if (snapshot) node->release(node->host, snapshot); unsigned width, height; size_t stride; uint32_t *out = vega_display_buffer(display, &width, &height, &stride); @@ -580,18 +601,25 @@ static int live(VegaInput *input, unsigned columns, unsigned rows, bool recorded if (pointer) show_pointer(out, stride, x, y, width, height); if (!show_frame(&grid, path, &differing, false)) { fputs("vega: the display did not take the frame\n", stderr); status = 1; break; } if (!open_input) break; - struct pollfd waits[32]; + struct pollfd waits[33]; unsigned count = vega_input_fds(input, waits, 32); - int timeout = screen.ticks ? 16 : -1; + if (node) waits[count++] = (struct pollfd){node->changes, POLLIN, 0}; /* the node saved: draw again */ + int timeout = screen.ticks && !node ? 16 : -1; if (due >= 0 && (timeout < 0 || due < timeout)) timeout = (int)due; if (poll(waits, count, timeout) <= 0) { + if (node) continue; app.now = vega_now(); if (screen.ticks) pure_app_frame(&app); continue; } - app.now = vega_now(); + if (!node) app.now = vega_now(); for (unsigned i = 0; i < count; ++i) { if (!(waits[i].revents & (POLLIN | POLLHUP | POLLERR))) continue; + if (node && waits[i].fd == node->changes) { + uint64_t saves; + if (read(node->changes, &saves, sizeof(saves)) < 0) { /* cleared already */ } + continue; + } VegaEvent events[64]; bool ended; unsigned n = vega_input_read(input, waits[i].fd, events, 64, &ended); @@ -610,6 +638,7 @@ static int live(VegaInput *input, unsigned columns, unsigned rows, bool recorded dump(&grid); if (path[0]) printf("frame %s differing %lu grid %ux%u cell %dx%d\n", path, differing, grid.width, grid.height, face.cell_width, face.cell_height); + fflush(stdout); } drawn = NULL; vega_grid_free(&grid); @@ -746,6 +775,77 @@ static bool open_face(const char *path, float size) { return ok; /* the bytes stay: the face reads them */ } +#ifdef VEGA_SURFACE +/* Vega in a Pure node (serve/surface.h): pure-serve ... --surface + * vega-surface.so -- display --symbols FONT.vsym [--size PX] [--card PATH] + * [--writeback WIDTHxHEIGHT --out PREFIX] [--live | --input EVENTS]. The + * display shows the node's app as of each save; keys and text go to its + * owner. Recorded events are read to their end, then the last frame is + * printed as text (on the node's standard output); live, it runs until the + * node stops. */ +__attribute__((visibility("default"))) int pure_surface_run(const PureSurface *surface, int argc, char **argv) { + if (surface->version != PURE_SURFACE_VERSION || surface->app_size != sizeof(PureApp) || + surface->module_size != sizeof(PureModule)) { + fputs("vega: this surface was built for another Pure\n", stderr); + return 2; + } + const char *symbols_path = NULL, *card = "/dev/dri/card1", *recorded = NULL; + unsigned width = 0, height = 0; + float size = 16; + bool writeback = false, live_input = false, usage = argc < 2 || strcmp(argv[1], "display"); + for (int i = 2; !usage && i < argc; i += 2) { + if (!strcmp(argv[i], "--live")) { + live_input = true; + --i; + continue; + } + if (i + 1 >= argc) usage = true; + else if (!strcmp(argv[i], "--symbols")) symbols_path = argv[i + 1]; + else if (!strcmp(argv[i], "--size") && sscanf(argv[i + 1], "%f", &size) == 1) {} + else if (!strcmp(argv[i], "--card")) card = argv[i + 1]; + else if (!strcmp(argv[i], "--input")) recorded = argv[i + 1]; + else if (!strcmp(argv[i], "--out")) frames_prefix = argv[i + 1]; + else if (!strcmp(argv[i], "--writeback") && sscanf(argv[i + 1], "%ux%u", &width, &height) == 2 && width && height) + writeback = true; + else usage = true; + } + if (usage || !symbols_path || (writeback && !frames_prefix) || (live_input && recorded)) { + fputs("vega-surface.so display --symbols FONT.vsym [--size PX] [--card PATH] [--writeback WIDTHxHEIGHT --out PREFIX]\n" + " [--live | --input EVENTS]\n", stderr); + return 2; + } + if (!open_face(symbols_path, size)) return 2; + char error[256]; + if (!(display = vega_display_open(card, writeback, writeback ? width : 0, writeback ? height : 0, error, sizeof(error)))) { + fprintf(stderr, "vega: %s\n", error); + return 2; + } + unsigned pixels_across, pixels_down; + size_t stride; + vega_display_buffer(display, &pixels_across, &pixels_down, &stride); + node = surface; + vega_screen(&screen, surface->module); + typing = screen.prompt >= 0; + const char *paths[1] = {recorded}; + /* Without --live or --input, no devices: the node's saves alone draw it. */ + VegaInput *input = vega_input_open(live_input || recorded ? paths : NULL, recorded ? 1 : 0, pixels_across, pixels_down); + int status = 2; + if (!input) fprintf(stderr, "vega: cannot read %s\n", recorded ? recorded : "input devices"); + else { + PureSnapshot *first = node->read(node->host, &app); /* app shows the node's states from here on */ + if (first) node->release(node->host, first); + status = first ? live(input, pixels_across / (unsigned)face.cell_width, pixels_down / (unsigned)face.cell_height, + recorded != NULL) + : 2; + vega_input_close(input); + } + pure_app_stop(&app); + vega_display_close(display); + display = NULL; + node = NULL; + return status; +} +#else int main(int argc, char **argv) { unsigned width = 0, height = 0, port = 8080; const char *symbols_path = NULL, *card = "/dev/dri/card1"; @@ -841,3 +941,4 @@ int main(int argc, char **argv) { vega_display_close(display); return status; } +#endif |
