summaryrefslogtreecommitdiff
path: root/ui/curses
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2026-07-31 04:04:37 +0200
committerPaul Oliver <contact@pauloliver.dev>2026-08-01 04:25:46 +0200
commit840bcbda4fa510554ea64cc943d33bd96e28b556 (patch)
treebb01d90b004b8a9550d3c3eaa57ac8c4cc727e35 /ui/curses
parent8a4563d6d0c9e81fdaa42f6f760cae2b49cad431 (diff)
Adds curses UI (WIP)HEADmaster
Diffstat (limited to 'ui/curses')
-rw-r--r--ui/curses/links.json1
-rw-r--r--ui/curses/ui.c440
2 files changed, 441 insertions, 0 deletions
diff --git a/ui/curses/links.json b/ui/curses/links.json
new file mode 100644
index 0000000..0cfb567
--- /dev/null
+++ b/ui/curses/links.json
@@ -0,0 +1 @@
+["-lncurses"]
diff --git a/ui/curses/ui.c b/ui/curses/ui.c
new file mode 100644
index 0000000..f1a3459
--- /dev/null
+++ b/ui/curses/ui.c
@@ -0,0 +1,440 @@
+// file : ui/curses/ui.c
+// project : Salis-VM
+// author : Paul Oliver <contact@pauloliver.dev>
+
+// index:
+// [section] includes
+// [section] macros/enums
+// [section] gfx globals
+// [section] tui globals
+// [section] gfx functions
+// [section] tui functions
+// [section] main
+
+// ----------------------------------------------------------------------------
+// [section] includes
+// ----------------------------------------------------------------------------
+#include <assert.h>
+#define NCURSES_WIDECHAR 1
+#include <curses.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <threads.h>
+#include <zlib.h>
+
+#include "arch.h"
+#include "arch_spec.h"
+#include "compress.h"
+#include "logger.h"
+#include "salis.h"
+
+// ----------------------------------------------------------------------------
+// [section] macros/enums
+// ----------------------------------------------------------------------------
+#define LOG_LINE_COUNT 0x400
+
+#define NCURSES_CTRL(x) (x & 0x1f)
+
+#define PANE_WIDTH 27
+#define PANE_WIDTH_AND_MARGIN (PANE_WIDTH + 2)
+#define PROC_FIELD_WIDTH 21
+#define PROC_PAGE_LINES 12
+
+#define FPS_MIN 10
+#define FPS_MAX 20
+
+enum UIPage {
+ PAGE_CORE,
+ PAGE_PROCESS,
+ PAGE_WORLD,
+ PAGE_IPC,
+ PAGE_LOG,
+ PAGE_COUNT,
+};
+
+enum UIColorPair {
+ PAIR_NORMAL,
+ PAIR_HEADER,
+ PAIR_LIVE_PROC,
+ PAIR_SELECTED_PROC,
+ PAIR_FREE_CELL,
+ PAIR_ALLOC_CELL,
+ PAIR_MEM_BLOCK_START,
+ PAIR_SELECTED_MB1,
+ PAIR_SELECTED_MB2,
+ PAIR_SELECTED_IP,
+ PAIR_SELECTED_SP,
+ PAIR_LOG_TRACE,
+ PAIR_LOG_INFO,
+ PAIR_LOG_ATTENTION,
+};
+
+enum UILogLevel {
+ TRACE,
+ INFO,
+ ATTENTION,
+};
+
+// ----------------------------------------------------------------------------
+// [section] gfx globals
+// ----------------------------------------------------------------------------
+static uint64_t g_gfx_vsiz;
+static uint64_t *g_gfx_inst;
+static uint64_t *g_gfx_mall;
+static uint64_t *g_gfx_mbst;
+static uint64_t *g_gfx_mb0s;
+static uint64_t *g_gfx_mb1s;
+static uint64_t *g_gfx_ipas;
+static uint64_t *g_gfx_spas;
+
+static const wchar_t *g_zoomed_symbols = (
+ L"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟"
+ L"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿"
+ L"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟"
+ L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿"
+);
+
+// ----------------------------------------------------------------------------
+// [section] tui globals
+// ----------------------------------------------------------------------------
+static bool g_exit;
+static bool g_running;
+static unsigned g_core;
+static unsigned g_page;
+static char *g_line_buff;
+static bool g_proc_genes;
+static uint64_t g_proc_scroll;
+static uint64_t g_proc_field_scroll;
+static uint64_t g_proc_gene_scroll;
+static uint64_t g_proc_selected;
+static uint64_t g_wrld_pos;
+static uint64_t g_wrld_zoom;
+static bool g_wcursor_mode;
+static int g_wcursor_x;
+static int g_wcursor_y;
+static uint64_t g_wcursor_pointed;
+static uint64_t g_log_cnt;
+static unsigned g_log_ptr;
+static unsigned g_log_scroll;
+static enum UILogLevel g_log_levels[LOG_LINE_COUNT];
+static char g_logs[LOG_LINE_COUNT][LOG_BUFFER_SIZE];
+static uint64_t g_vlin;
+static uint64_t g_vsiz;
+static uint64_t g_vlin_rng;
+static uint64_t g_vsiz_rng;
+static uint64_t g_ivpt_scroll;
+static uint64_t g_step_block;
+static float g_steps_per_sec;
+
+// ----------------------------------------------------------------------------
+// [section] gfx functions
+// ----------------------------------------------------------------------------
+static void gfx_init(uint64_t vsiz) {
+ assert(vsiz);
+
+ g_gfx_vsiz = vsiz;
+ g_gfx_inst = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mall = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mbst = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mb0s = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mb1s = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_ipas = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_spas = calloc(g_gfx_vsiz, sizeof(uint64_t));
+
+ assert(g_gfx_inst);
+ assert(g_gfx_mall);
+ assert(g_gfx_mbst);
+ assert(g_gfx_mb0s);
+ assert(g_gfx_mb1s);
+ assert(g_gfx_ipas);
+ assert(g_gfx_spas);
+}
+
+static void gfx_free(void) {
+ if (g_gfx_vsiz == 0) {
+ return;
+ }
+
+ assert(g_gfx_inst);
+ assert(g_gfx_mall);
+ assert(g_gfx_mbst);
+ assert(g_gfx_mb0s);
+ assert(g_gfx_mb1s);
+ assert(g_gfx_ipas);
+ assert(g_gfx_spas);
+
+ g_gfx_vsiz = 0;
+
+ free(g_gfx_inst);
+ free(g_gfx_mall);
+ free(g_gfx_mbst);
+ free(g_gfx_mb0s);
+ free(g_gfx_mb1s);
+ free(g_gfx_ipas);
+ free(g_gfx_spas);
+
+ g_gfx_inst = NULL;
+ g_gfx_mall = NULL;
+ g_gfx_mbst = NULL;
+ g_gfx_mb0s = NULL;
+ g_gfx_mb1s = NULL;
+ g_gfx_ipas = NULL;
+ g_gfx_spas = NULL;
+}
+
+static void gfx_resize(uint64_t vsiz) {
+ assert(vsiz);
+ gfx_free();
+ gfx_init(vsiz);
+}
+
+static void gfx_render_inst(const struct Core *core, uint64_t pos, uint64_t zoom) {
+ assert(core);
+
+ for (uint64_t i = 0; i < g_gfx_vsiz; i++) {
+ g_gfx_inst[i] = 0;
+ g_gfx_mall[i] = 0;
+
+ for (uint64_t j = 0; j < zoom; j++) {
+ uint64_t addr = pos + (i * zoom) + j;
+ g_gfx_inst[i] += mvec_get_byte(core, addr);
+ g_gfx_mall[i] += mvec_is_alloc(core, addr) ? 1 : 0;
+ }
+ }
+}
+
+static void gfx_clear_array(uint64_t *arry) {
+ assert(arry);
+ memset(arry, 0, g_gfx_vsiz * sizeof(uint64_t));
+}
+
+#if defined(ARCH_SPEC_MVEC_LOOP)
+static void gfx_accumulate_pixel(uint64_t pos, uint64_t zoom, uint64_t pixa, uint64_t *arry) {
+ assert(arry);
+ uint64_t beg_mod = pos % MVEC_SIZE;
+ uint64_t end_mod = beg_mod + (g_gfx_vsiz * zoom);
+ uint64_t pix_mod = pixa % MVEC_SIZE;
+
+#if !defined(NDEBUG)
+ uint64_t inc_cnt = 0;
+#endif
+
+ while (pix_mod < end_mod) {
+ if (pix_mod >= beg_mod && pix_mod < end_mod) {
+ uint64_t pixi = (pix_mod - beg_mod) / zoom;
+ assert(pixi < g_gfx_vsiz);
+ arry[pixi]++;
+
+#if !defined(NDEBUG)
+ inc_cnt++;
+#endif
+ }
+
+ pix_mod += MVEC_SIZE;
+ }
+
+#if !defined(NDEBUG)
+ if (zoom != 1) {
+ assert(inc_cnt <= 2);
+ }
+#endif
+}
+#else
+static void gfx_accumulate_pixel(uint64_t pos, uint64_t zoom, uint64_t pixa, uint64_t *arry) {
+ assert(arry);
+ uint64_t end = pos + (g_gfx_vsiz * zoom);
+
+ if (pixa < pos || pixa >= end) {
+ return;
+ }
+
+ uint64_t pixi = (pixa - pos) / zoom;
+ assert(pixi < g_gfx_vsiz);
+ arry[pixi]++;
+}
+#endif
+
+static void gfx_render_mbst(const struct Core *core, uint64_t pos, uint64_t zoom) {
+ assert(core);
+ gfx_clear_array(g_gfx_mbst);
+
+ for (uint64_t pix = core->pfst; pix <= core->plst; pix++) {
+ uint64_t mb0a = arch_proc_get_mb0_addr(core, pix);
+ uint64_t mb1a = arch_proc_get_mb1_addr(core, pix);
+ gfx_accumulate_pixel(pos, zoom, mb0a, g_gfx_mbst);
+
+ if (arch_proc_get_mb1_size(core, pix)) {
+ gfx_accumulate_pixel(pos, zoom, mb1a, g_gfx_mbst);
+ }
+ }
+}
+
+static void gfx_render_mb0s(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+ gfx_clear_array(g_gfx_mb0s);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ uint64_t mb0a = arch_proc_get_mb0_addr(core, psel);
+ uint64_t mb0s = arch_proc_get_mb0_size(core, psel);
+
+ for (uint64_t i = 0; i < mb0s; i++) {
+ gfx_accumulate_pixel(pos, zoom, mb0a + i, g_gfx_mb0s);
+ }
+}
+
+static void gfx_render_mb1s(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+ gfx_clear_array(g_gfx_mb1s);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ uint64_t mb1a = arch_proc_get_mb1_addr(core, psel);
+ uint64_t mb1s = arch_proc_get_mb1_size(core, psel);
+
+ for (uint64_t i = 0; i < mb1s; i++) {
+ gfx_accumulate_pixel(pos, zoom, mb1a + i, g_gfx_mb1s);
+ }
+}
+
+static void gfx_render_ipas(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+ gfx_clear_array(g_gfx_ipas);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ gfx_accumulate_pixel(pos, zoom, arch_proc_get_ip_addr(core, psel), g_gfx_ipas);
+}
+
+static void gfx_render_spas(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+ gfx_clear_array(g_gfx_spas);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ gfx_accumulate_pixel(pos, zoom, arch_proc_get_sp_addr(core, psel), g_gfx_spas);
+}
+
+static void gfx_render(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+ gfx_render_inst(core, pos, zoom);
+ gfx_render_mbst(core, pos, zoom);
+ gfx_render_mb0s(core, pos, zoom, psel);
+ gfx_render_mb1s(core, pos, zoom, psel);
+ gfx_render_ipas(core, pos, zoom, psel);
+ gfx_render_spas(core, pos, zoom, psel);
+}
+
+// ----------------------------------------------------------------------------
+// [section] tui functions
+// ----------------------------------------------------------------------------
+static void tui_line_buff_free(void) {
+ if (g_line_buff) {
+ free(g_line_buff);
+ }
+
+ g_line_buff = NULL;
+}
+
+static void tui_line_buff_resize(void) {
+ tui_line_buff_free();
+ g_line_buff = calloc(COLS + 1, sizeof(char));
+}
+
+static void tui_line(bool clear, int line, int color, int attr, const char *format, ...) {
+ assert(line >= 0);
+ assert(format);
+
+ if (line >= LINES) {
+ return;
+ }
+
+ if (clear) {
+ move(line, 0);
+ clrtoeol();
+ }
+
+ va_list args;
+ va_start(args, format);
+ attron(COLOR_PAIR(color) | attr);
+ vsnprintf(g_line_buff, COLS, format, args);
+ mvprintw(line, 1, "%s", g_line_buff);
+ attroff(COLOR_PAIR(color) | attr);
+ va_end(args);
+}
+
+static void tui_clear_line(int l) {
+ tui_line(true, l, PAIR_NORMAL, A_NORMAL, "");
+}
+
+static void tui_field(int line, int col, int color, int attr, const char *format, ...) {
+ assert(line >= 0);
+ assert(col >= 0);
+ assert(format);
+
+ if (line >= LINES || col >= COLS) {
+ return;
+ }
+
+ va_list args;
+ va_start(args, format);
+ attron(COLOR_PAIR(color) | attr);
+ vsnprintf(g_line_buff, COLS - col, format, args);
+ mvprintw(line, col, "%s", g_line_buff);
+ attroff(COLOR_PAIR(color) | attr);
+ va_end(args);
+}
+
+static void tui_str_field(int l, const char *label, const char *value) {
+ assert(label);
+ assert(strlen(label) <= 4);
+ assert(value);
+ tui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %18s", label, value);
+}
+
+static void tui_ulx_field(int l, const char *label, uint64_t value) {
+ assert(label);
+ assert(strlen(label) <= 4);
+ tui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18lx", label, value);
+}
+
+static void tui_uld_field(int l, const char *label, uint64_t value) {
+ assert(label);
+ assert(strlen(label) <= 4);
+ tui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18ld", label, value);
+}
+
+static void tui_print_core(int l) {
+ tui_line(false, ++l, PAIR_HEADER, A_BOLD, "CORE [%d]", g_core);
+ tui_ulx_field(++l, "cycl", g_cores[g_core].cycl);
+ tui_ulx_field(++l, "mall", g_cores[g_core].mall);
+ tui_ulx_field(++l, "mut0", g_cores[g_core].muta[0]);
+ tui_ulx_field(++l, "mut1", g_cores[g_core].muta[1]);
+ tui_ulx_field(++l, "mut2", g_cores[g_core].muta[2]);
+ tui_ulx_field(++l, "mut3", g_cores[g_core].muta[3]);
+ tui_ulx_field(++l, "pnum", g_cores[g_core].pnum);
+ tui_ulx_field(++l, "pcap", g_cores[g_core].pcap);
+ tui_ulx_field(++l, "pfst", g_cores[g_core].pfst);
+ tui_ulx_field(++l, "plst", g_cores[g_core].plst);
+ tui_ulx_field(++l, "pcur", g_cores[g_core].pcur);
+ tui_ulx_field(++l, "psli", g_cores[g_core].psli);
+ tui_ulx_field(++l, "ivpt", g_cores[g_core].ivpt);
+}
+
+// ----------------------------------------------------------------------------
+// [section] main
+// ----------------------------------------------------------------------------
+int main(void) {
+ return 0;
+}