summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorWormHeamer2025-11-06 22:58:25 -0500
committerWormHeamer2025-11-06 22:58:25 -0500
commitd7479de95b8c4e8ada7db3676424fb0b2e1713cf (patch)
treec12c6a7c0252397442934adce78c83abf74c7062 /main.c
parent1f31f94c16eab34b931cc8a8973a36a78ea74046 (diff)
separate vui.c, vui.h
Diffstat (limited to 'main.c')
-rw-r--r--main.c1033
1 files changed, 10 insertions, 1023 deletions
diff --git a/main.c b/main.c
index b3503b8..54cfb9d 100644
--- a/main.c
+++ b/main.c
@@ -1,921 +1,5 @@
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <stdarg.h>
-#include <stdbit.h>
-#include <errno.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#include <poll.h>
-#include <time.h>
-
#include "wrmr.h"
-
-#define CSI "\x1b["
-
-/* TODO
- *
- * - clean up keyboard input
- * - separate out library interface
- * - maybe some custom colors
- */
-
-typedef enum {
- KEY_EOF = 0,
- KEY_BACKSPACE = 0x08,
- KEY_RET = 0x0d,
- KEY_ESC = 0x1b,
- KEY_DEL = 0x7f,
- KEY_UTF8_MAX = 0x10ffff,
-
- KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN,
- KEY_HOME, KEY_END, KEY_PGUP, KEY_PGDN,
-
- KEY_F1, KEY_F2, KEY_F3, KEY_F4,
- KEY_F5, KEY_F6, KEY_F7, KEY_F8,
- KEY_F9, KEY_F10, KEY_F11, KEY_F12,
-
- KEY_SHIFT_BIT = 0x200000,
- KEY_CTRL_BIT = 0x400000,
- KEY_ALT_BIT = 0x800000,
- KEY_META_BIT = 0x1000000,
- KEY_INVALID = 0x7fffffff,
-
- KEY_CTRL_MASK = 0xe000000,
- KEY_BASE_MASK = ~KEY_CTRL_MASK,
-} VuiKey;
-
-typedef enum {
- FG_BLACK = 0,
- FG_RED = 1,
- FG_GREEN = 2,
- FG_YELLOW = 3,
- FG_BLUE = 4,
- FG_MAGENTA = 5,
- FG_CYAN = 6,
- FG_WHITE = 7,
-
- FG_BBLACK = FG_BLACK + 8,
- FG_BRED = FG_RED + 8,
- FG_BGREEN = FG_GREEN + 8,
- FG_BYELLOW = FG_YELLOW + 8,
- FG_BBLUE = FG_BLUE + 8,
- FG_BMAGENTA = FG_MAGENTA + 8,
- FG_BCYAN = FG_CYAN + 8,
- FG_BWHITE = FG_WHITE + 8,
-
- BG_BLACK = FG_BLACK << 4,
- BG_RED = FG_RED << 4,
- BG_GREEN = FG_GREEN << 4,
- BG_YELLOW = FG_YELLOW << 4,
- BG_BLUE = FG_BLUE << 4,
- BG_MAGENTA = FG_MAGENTA << 4,
- BG_CYAN = FG_CYAN << 4,
- BG_WHITE = FG_WHITE << 4,
-
- BG_BBLACK = FG_BBLACK << 4,
- BG_BRED = FG_BRED << 4,
- BG_BGREEN = FG_BGREEN << 4,
- BG_BYELLOW = FG_BYELLOW << 4,
- BG_BBLUE = FG_BBLUE << 4,
- BG_BMAGENTA = FG_BMAGENTA << 4,
- BG_BCYAN = FG_BCYAN << 4,
- BG_BWHITE = FG_BWHITE << 4,
-
- A_BOLD = 1 << 8,
- A_DIM = 1 << 9,
- A_ITALIC = 1 << 10,
- A_UNDERSCORE = 1 << 11,
- A_BLINK = 1 << 12,
- A_REVERSE = 1 << 13,
-} VuiAttr;
-
-#define ATTR_FG(a) ((a) & 0xf)
-#define ATTR_BG(a) (((a)>>4) & 0xf)
-#define ATTR_A(a) ((a) & ~0xff)
-#define ATTR_DEFAULT (FG_WHITE | BG_BLACK)
-
-typedef uint32_t VuiChar;
-
-typedef struct {
- unsigned width, height;
- VuiChar *chr;
- uint16_t *attr;
-} VuiBuffer;
-
-typedef struct {
- unsigned width, height;
- VuiBuffer buf1, buf2;
- VuiBuffer *front, *back;
- void (*redraw_fn)(void *ctx);
- void *redraw_ctx;
- int redraw_all;
- int scroll_x, scroll_y;
- int curs_vis, curs_x, curs_y;
-} VuiWindow;
-
-static struct termios vui_init_stdin_tos, vui_init_stdout_tos, vui_raw_stdin_tos, vui_raw_stdout_tos;
-VuiWindow vui_win = {
- .front = &vui_win.buf1,
- .back = &vui_win.buf2,
-};
-
-#define LINES (vui_win.height)
-#define COLS (vui_win.width)
-
-#define BCHR(b,x,y) ((b)->chr[(x) + (y) * (b)->width])
-#define BATTR(b,x,y) ((b)->attr[(x) + (y) * (b)->width])
-#define CHR(x,y) BCHR(vui_win.front, x, y)
-#define ATTR(x,y) BATTR(vui_win.front, x, y)
-
-void vui_getwinsz(unsigned *w, unsigned *h) {
- struct winsize wsz;
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsz);
- *w = wsz.ws_col;
- *h = wsz.ws_row;
-}
-
-static void vui_clrspan(VuiBuffer *buf, unsigned x0, unsigned x1, unsigned y) {
- for (unsigned x = x0; x < x1; x++) {
- BCHR(buf, x, y) = ' ';
- }
- for (unsigned x = x0; x < x1; x++) {
- BATTR(buf, x, y) = ATTR_DEFAULT;
- }
-}
-
-static void vui_clrtoeol(VuiBuffer *buf, unsigned x0, unsigned y) {
- vui_clrspan(buf, x0, buf->width, y);
-}
-
-static void clear_buf(VuiBuffer *buf) {
- for (unsigned y = 0; y < buf->height; y++) {
- vui_clrtoeol(buf, 0, y);
- }
-}
-
-static void vui_clear(void) {
- clear_buf(vui_win.front);
-}
-
-void vui_fill_rect(VuiChar c, VuiAttr a, int x0, int y0, int width, int height) {
- if (x0 < 0) { width += x0; x0 = 0; }
- if (y0 < 0) { height += y0; y0 = 0; }
- if (x0 + width >= COLS) width = (COLS - 1) - x0;
- if (y0 + height >= LINES) height = (LINES - 1) - y0;
- if (width < 1 || height < 1) return;
- for (int y = y0; y < y0 + height; y++) {
- for (int x = x0; x < x0 + width; x++) {
- CHR(x, y) = c;
- ATTR(x, y) = a;
- }
- }
-}
-
-void vui_fill(VuiChar c, VuiAttr a) {
- vui_fill_rect(c, a, 0, 0, COLS, LINES);
-}
-
-static void resize_buf(VuiBuffer *buf, unsigned nw, unsigned nh) {
- VuiChar *nchr = calloc(nw * nh, sizeof(*buf->chr));
- uint16_t *nattr = calloc(nw * nh, sizeof(*buf->attr));
- assert(nchr);
- assert(nattr);
-
- unsigned oldw = buf->width;
- unsigned oldh = buf->height;
- unsigned minw = nw < oldw ? nw : oldw;
- unsigned minh = nh < oldh ? nh : oldh;
- for (unsigned y = 0; y < minh; y++) {
- memcpy(nchr + y*nw, buf->chr + y*oldw, minw * sizeof(*buf->chr));
- memcpy(nattr + y*nw, buf->attr + y*oldw, minw * sizeof(*buf->attr));
- }
-
- free(buf->chr);
- free(buf->attr);
- buf->chr = nchr;
- buf->attr = nattr;
- buf->width = nw;
- buf->height = nh;
- for (unsigned y = 0; y < minh; y++) vui_clrtoeol(buf, minw, y);
- for (unsigned y = minh; y < buf->height; y++) vui_clrtoeol(buf, 0, y);
-}
-
-static void vui_resize(unsigned nw, unsigned nh) {
- resize_buf(&vui_win.buf1, nw, nh);
- resize_buf(&vui_win.buf2, nw, nh);
- vui_win.width = nw;
- vui_win.height = nh;
-}
-
-static void vui_adjust(void) {
- unsigned w, h;
- vui_getwinsz(&w, &h);
- if (w != COLS || h != LINES) {
- vui_resize(w, h);
- vui_win.redraw_all = 1;
- }
-}
-
-void vui_curs_vis(int vis) {
- vui_win.curs_vis = vis;
- if (vis) {
- fputs(CSI "?25h", stdout);
- } else {
- fputs(CSI "?25l", stdout);
- }
-}
-
-void vui_curs_pos(int x, int y) {
- vui_win.curs_x = x;
- vui_win.curs_y = y;
-}
-
-void vui_on_sigwinch(int _) {
- vui_adjust();
- if (vui_win.redraw_fn) {
- vui_win.redraw_fn(vui_win.redraw_ctx);
- }
-}
-
-void vui_init(void) {
- tcgetattr(STDIN_FILENO, &vui_init_stdin_tos);
- vui_raw_stdin_tos = vui_init_stdin_tos;
- /*
- vui_raw_stdin_tos.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
- vui_raw_stdin_tos.c_lflag |= IGNBRK;
- */
-
- vui_raw_stdin_tos.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
- vui_raw_stdin_tos.c_oflag &= ~OPOST;
- vui_raw_stdin_tos.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- vui_raw_stdin_tos.c_cflag &= ~(CSIZE | PARENB);
- vui_raw_stdin_tos.c_cflag |= CS8;
-
- tcsetattr(STDIN_FILENO, TCSANOW, &vui_raw_stdin_tos);
-
- tcgetattr(STDOUT_FILENO, &vui_init_stdout_tos);
- vui_raw_stdout_tos = vui_init_stdout_tos;
- vui_raw_stdout_tos.c_oflag &= ~OPOST;
- tcsetattr(STDOUT_FILENO, TCSANOW, &vui_raw_stdout_tos);
-
- /* set white:black to default */
- printf(CSI "40;37m" CSI "8]");
- vui_curs_vis(0);
- vui_win.redraw_all = 1;
-
- vui_adjust();
- signal(SIGWINCH, vui_on_sigwinch);
-}
-
-
-static void free_buf(VuiBuffer *buf) {
- free(buf->chr);
- free(buf->attr);
- buf->chr = NULL;
- buf->attr = NULL;
- buf->width = 0;
- buf->height = 0;
-}
-
-static inline int bchr_equiv(VuiBuffer *a, VuiBuffer *b, unsigned i) {
- return a->chr[i] == b->chr[i] && a->attr[i] == b->attr[i];
-}
-
-static char *vui_out = NULL;
-static size_t vui_outn = 0;
-static size_t vui_out_cap = 0;
-
-void vui_fini(void) {
- vui_curs_vis(1);
- free_buf(&vui_win.buf1);
- free_buf(&vui_win.buf2);
- free(vui_out);
- vui_out = NULL;
- vui_out_cap = 0;
- printf(CSI "0m" CSI "H" CSI "2J");
- tcsetattr(STDIN_FILENO, TCSANOW, &vui_init_stdin_tos);
- tcsetattr(STDOUT_FILENO, TCSANOW, &vui_init_stdout_tos);
-}
-
-void vui_out_fit(size_t n) {
- size_t c = stdc_bit_ceil(n);
- if (c > vui_out_cap) {
- char *p = realloc(vui_out, c);
- if (!p) {
- vui_fini();
- fprintf(stderr, "failed to reallocate vui output buffer\n");
- exit(1);
- }
- vui_out = p;
- vui_out_cap = c;
- }
-}
-
-void vui_outf(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- int n = vsnprintf(NULL, 0, fmt, ap);
- va_end(ap);
- va_start(ap, fmt);
- vui_out_fit(vui_outn + n);
- vsprintf(&vui_out[vui_outn], fmt, ap);
- vui_outn += n;
- va_end(ap);
-}
-
-
-/* lookup table from utf-8 codepoint to byte length:
- * { 6,6,6,6,6,6,5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,2,2,2,2,1,1,1,1,1,1,1,1, }
- * len(cp) = tbl[32 - clz(cp)].
- *
- * how could we possibly reduce this?
- * 6 sixes, 5 fives, 5 fours, 5 threes, four twos, and eight ones.
- * no obvious pattern.
- *
- * to avoid the subtraction we reverse:
- * { 1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,6, }
- *
- * subtract one from each:
- * { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,5, }
- *
- * clamp to <=3 (no unicode codepoints are actually that long):
- * { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, }
- *
- * concatenate bits:
- * 0b0000000000000001010101101010101011111111111111111111111111111111
- *
- * convert to hex, and now:
- * len(cp) = 1 + (0x156AAFFFFFFFF >> (2 * clz(cp))) & 3
- */
-
-#define UTF8_CP_LEN_BITS ((uint64_t)0x156AAFFFFFFFF)
-#define UTF8_CP_SHIFT(cp) (stdc_leading_zeros((uint32_t)cp) << 1)
-#define UTF8_CP_LEN(cp) (1 + ((UTF8_CP_LEN_BITS >> UTF8_CP_SHIFT(cp)) & 3))
-
-static inline void vui_outc(char c) {
- vui_out_fit(vui_outn + 1);
- vui_out[vui_outn++] = c;
-}
-
-/* don't pass a NUL to this */
-/* it doesn't make sense to do so, and assuming non-zero lets us dodge a branch
- * in stdc_leading_zeros() */
-static inline void vui_outvc(VuiChar c) {
- ASSUME(c > 0 && c <= 0x110000);
- uint8_t len = UTF8_CP_LEN(c);
- vui_out_fit(vui_outn + len);
- ASSUME(len > 0 && len < 5);
-
- if (len == 1) {
- vui_out[vui_outn++] = c;
- return;
- }
-
- for (unsigned i = len; --i;) {
- vui_out[vui_outn + i] = 0x80 | (c & 0x3f);
- c >>= 6;
- }
-
- vui_out[vui_outn] = (0xf0 << (4 - len)) | c;
- vui_outn += len;
-}
-
-static inline void vui_outvcn(VuiChar *c, size_t n) {
- while (n--) vui_outvc(*c++);
-}
-
-static inline void vui_outsn(const char *s, unsigned n) {
- vui_out_fit(vui_outn + n);
- memcpy(&vui_out[vui_outn], s, n);
- vui_outn += n;
-}
-
-static inline void vui_outs(const char *s) {
- vui_outsn(s, strlen(s));
-}
-
-static inline void vui_out_flush(void) {
- fwrite(vui_out, 1, vui_outn, stdout);
- fflush(stdout);
-#if 0
- static unsigned out_frame = 0;
- FILE *f = fopen("out_log.txt", "a");
- assert(f);
- fprintf(f, "\n\n:: OUTPUT FRAME %u ::\n", ++out_frame);
- fwrite(vui_out, 1, vui_outn, f);
- fclose(f);
-#endif
- vui_outn = 0;
-}
-
-static inline void curs_move(unsigned *restrict ptr_x, unsigned *restrict ptr_y, unsigned dst_x, unsigned dst_y) {
- unsigned src_x = *ptr_x;
- unsigned src_y = *ptr_y;
- if (src_x != dst_x && src_y != dst_y) {
- if (dst_x == src_x - 1) {
- vui_outf(CSI "\b%ud", dst_y + 1);
- } else if (dst_x > 0) {
- vui_outf(CSI "%u;%uH", dst_y + 1, dst_x + 1);
- } else if (dst_y > 0) {
- vui_outf(CSI "%uH", dst_y + 1);
- } else {
- vui_outs(CSI "H");
- }
- } else if (src_x != dst_x) {
- if (dst_x == 0) {
- vui_outc('\r');
- } else if (dst_x == src_x - 1) {
- vui_outc('\b');
- } else {
- vui_outf(CSI "%uG", dst_x + 1);
- }
- } else if (src_y != dst_y) {
- if (dst_y == src_y - 1 && dst_x == src_x + 1) {
- vui_outs("\x1b" "M");
- abort();
- } else if (dst_y == src_y + 1 && dst_x == src_x + 1) {
- vui_outs("\x1b" "D");
- abort();
- } else if (dst_y > 0) {
- vui_outf(CSI "%ud", dst_y + 1);
- } else {
- vui_outs(CSI "d");
- }
- }
- *ptr_x = dst_x;
- *ptr_y = dst_y;
-}
-
-#define M(s) do {\
- if (sep) vui_outc(';');\
- vui_outs(s);\
- sep = 1;\
-} while(0)
-
-static void attr_chg(VuiAttr *ptr, VuiAttr to) {
- VuiAttr from = *ptr;
- if (from == to) return;
-
- int attr_chg_count = stdc_count_ones(ATTR_A(from) ^ ATTR_A(to));
- int chg_attr = (attr_chg_count != 0);
- if (!chg_attr) goto chg_colors;
-
- /* deduct color changes */
- attr_chg_count -= (ATTR_FG(to) == ATTR_FG(from)) && (ATTR_FG(to) != ATTR_DEFAULT);
- attr_chg_count -= (ATTR_BG(to) == ATTR_BG(from)) && (ATTR_BG(to) != ATTR_DEFAULT);
-
- int should_rebuild = (attr_chg_count > 1) || to == ATTR_DEFAULT;
-
- if (should_rebuild) {
- vui_outs(CSI "0");
- if (to & A_BOLD) vui_outs(";1");
- if (to & A_DIM) vui_outs(";2");
- if (to & A_ITALIC) vui_outs(";3");
- if (to & A_UNDERSCORE) vui_outs(";4");
- if (to & A_BLINK) vui_outs(";5");
- if (to & A_REVERSE) vui_outs(";7");
- from = ATTR_DEFAULT;
- assert(ATTR_FG(from) == FG_WHITE);
- assert(ATTR_BG(from) == BG_BLACK);
- } else {
- int sep = 0;
- vui_outs(CSI);
- if ((to ^ from) & A_BOLD) {
- if (to & A_BOLD) M("1");
- else {
- M("22");
- from &= (~A_BOLD & ~A_DIM);
- }
- }
- if ((to ^ from) & A_DIM) {
- if (to & A_BOLD) M("2");
- else {
- M("22");
- from &= (~A_BOLD & ~A_DIM);
- }
- }
- if ((to ^ from) & A_ITALIC) M((to & A_ITALIC) ? "3" : "23");
- if ((to ^ from) & A_UNDERSCORE) M((to & A_UNDERSCORE) ? "4" : "24");
- if ((to ^ from) & A_BLINK) M((to & A_BLINK) ? "5" : "25");
- if ((to ^ from) & A_REVERSE) M((to & A_REVERSE) ? "7" : "27");
- }
-
-chg_colors:;
-
- int f_fg = ATTR_FG(from);
- int f_bg = ATTR_BG(from);
- int t_fg = ATTR_FG(to);
- int t_bg = ATTR_BG(to);
-
- int chg_fg = (t_fg != f_fg);
- int chg_bg = (t_bg != f_bg);
- if (chg_fg || chg_bg) {
- if (chg_attr) vui_outc(';');
- else vui_outs(CSI);
- if (chg_fg) {
- vui_outc(t_fg > 7 ? '9' : '3');
- vui_outc((t_fg & 7) + '0');
- }
- if (chg_bg) {
- if (chg_fg) vui_outc(';');
- if (t_bg > 7) {
- vui_outs("10");
- } else {
- vui_outc('4');
- }
- vui_outc((t_bg & 7) + '0');
- }
- }
-
- if (chg_fg || chg_bg || chg_attr) vui_outc('m');
- *ptr = to;
-}
-
-unsigned vui_changes = 0;
-unsigned vui_max_change = 0;
-unsigned vui_out_chars = 0;
-int scrolled_x = 0, scrolled_y = 0;
-
-void vui_scroll_buf(VuiBuffer *b, int dx, int dy);
-
-void vui_blit(void) {
- VuiBuffer *front = vui_win.front;
- VuiBuffer *back = vui_win.back;
-
- // vui_outf(CSI "H" CSI "0m");
- // vui_outs(CSI "2J");
- // vui_outs(CSI "3J");
- static VuiAttr attr_last = ATTR_DEFAULT;
- static unsigned cur_x = 0;
- static unsigned cur_y = 0;
-
- /*
- vui_outs(CSI "H" CSI "0m");
- attr_last = ATTR_DEFAULT;
- cur_x = 0;
- cur_y = 0;
- */
-
- scrolled_x = vui_win.scroll_x;
- scrolled_y = vui_win.scroll_y;
-
- //vui_win.redraw_all = 1;
- if (vui_win.redraw_all) {
- vui_outs(CSI "H" CSI "2J" CSI "0m");
- attr_last = ATTR_DEFAULT;
- vui_changes = COLS * LINES;
- unsigned max = COLS * LINES;
- for (unsigned i = 0; i < max; i++) {
- attr_chg(&attr_last, front->attr[i]);
- vui_outvc(front->chr[i]);
- }
- vui_outs(CSI "H");
- cur_x = 0;
- cur_y = 0;
- vui_win.redraw_all = 0;
- vui_win.scroll_x = 0;
- vui_win.scroll_y = 0;
- goto end;
- }
-
- vui_changes = 0;
- vui_max_change = 0;
-
- if (vui_win.scroll_x || vui_win.scroll_y) {
- if (abs(vui_win.scroll_x) >= COLS || abs(vui_win.scroll_y) > LINES) {
- vui_outs(CSI "2J");
- goto scrolled;
- }
-
- /* spaces inserted by scrolling will have the last-used attribute */
- /* which we don't really want */
- attr_chg(&attr_last, ATTR_DEFAULT);
-
- if (vui_win.scroll_x < 0) {
- /* delete chars at start of line */
- for (unsigned y = 0; y < LINES; y++) {
- curs_move(&cur_x, &cur_y, 0, y);
- vui_outf(CSI "%dP", -vui_win.scroll_x);
- }
- } else if (vui_win.scroll_x > 0) {
- /* insert spaces at start of line */
- for (unsigned y = 0; y < LINES; y++) {
- curs_move(&cur_x, &cur_y, 0, y);
- vui_outf(CSI "%d@", vui_win.scroll_x);
- }
- }
-
- /* xterm has \x1b[S and \x1b[T to scroll directly */
- /* but these don't work in linux vtty */
- if (vui_win.scroll_y < 0) {
- /* delete lines at top */
- curs_move(&cur_x, &cur_y, 0, 0);
- if (vui_win.scroll_y == -1) vui_outs(CSI "M");
- else vui_outf(CSI "%dM", -vui_win.scroll_y);
- } else if (vui_win.scroll_y > 0) {
- /* insert blank lines at top */
- curs_move(&cur_x, &cur_y, 0, 0);
- if (vui_win.scroll_y == 1) vui_outs(CSI "L");
- else vui_outf(CSI "%dL", vui_win.scroll_y);
- }
-
-scrolled:
- vui_scroll_buf(back, vui_win.scroll_x, vui_win.scroll_y);
- vui_win.scroll_x = 0;
- vui_win.scroll_y = 0;
- }
-
- for (unsigned y = 0; y < LINES; y++) {
- unsigned x = 0;
- while (x < COLS) {
- while (x < COLS && bchr_equiv(back, front, (y * COLS) + x)) x++;
- if (x >= COLS) break;
- unsigned x0 = x;
- VuiAttr a = ATTR(x0, y);
- while (x < COLS && !bchr_equiv(back, front, (y * COLS) + x) && BATTR(front, x, y) == a) x++;
- if (x0 != x) {
- vui_changes++;
- curs_move(&cur_x, &cur_y, x0, y);
- attr_chg(&attr_last, a);
- vui_outvcn(&CHR(x0, y), x - x0);
- cur_x = x;
- if (x - x0 > vui_max_change) vui_max_change = x - x0;
- }
- }
- }
-
-end:
- if (vui_win.curs_vis) {
- curs_move(&cur_x, &cur_y, vui_win.curs_x, vui_win.curs_y);
- }
-
- vui_out_chars = vui_outn;
- vui_out_flush();
- memcpy(back->chr, front->chr, sizeof(*back->chr) * (back->width * back->height));
- memcpy(back->attr, front->attr, sizeof(*back->attr) * (back->width * back->height));
- vui_win.front = back;
- vui_win.back = front;
-}
-
-void vui_chra(int x, int y, VuiChar c, VuiAttr a) {
- if (x >= 0 && x < (int)COLS && y >= 0 && y < (int)LINES) {
- CHR(x, y) = c;
- ATTR(x, y) = a;
- }
-}
-
-void vui_chr(int x, int y, VuiChar c) {
- vui_chra(x, y, c, ATTR_DEFAULT);
-}
-
-/*
-static inline u32 utf8_next(u32 *p, const char *s, u32 n) {
- u32 i = *p;
- u8 c = s[i++];
- usize bits = stdc_leading_ones(c);
- ASSUME(bits < 5);
- u32 cp = c & ((1 << (7-bits)) - 1);
- while (bits-- > 1) {
- c = s[i++];
- cp = (cp << 6) | (c & 0x3F);
- }
- *p = i;
- return cp;
-}
-*/
-
-static inline const char *utf8_next(u32 *out, const char *src) {
- u8 c = *src++;
- usize bits = stdc_leading_ones(c);
- ASSUME(bits < 5);
- u32 cp = c & (-1 >> bits);
- while (bits-- > 1) {
- c = *src++;
- cp = (cp << 6) | (c & 0x3F);
- }
- *out = cp;
- return src;
-}
-
-void utf8_decode(uint32_t *dst, const char *src, unsigned n) {
- const char *end = src + n;
- while (src < end) src = utf8_next(dst++, src);
-}
-
-unsigned vui_putsna(int x, int y, const char *s, unsigned n, VuiAttr a) {
- if (n > COLS - x) n = COLS - x;
- utf8_decode(&CHR(x, y), s, n);
- for (uint16_t *pa = &ATTR(x, y); n--;) *pa++ = a;
- return n;
-}
-
-unsigned vui_putsn(int x, int y, const char *s, unsigned n) {
- return vui_putsna(x, y, s, n, ATTR_DEFAULT);
-}
-
-unsigned vui_putsa(int x, int y, const char *s, VuiAttr a) {
- return vui_putsna(x, y, s, strlen(s), a);
-}
-
-unsigned vui_puts(int x, int y, const char *s) {
- return vui_putsn(x, y, s, strlen(s));
-}
-
-int vui_avprintf(int x, int y, VuiAttr a, const char *fmt, va_list ap) {
- va_list ap2;
- va_copy(ap2, ap);
- int n = vsnprintf(NULL, 0, fmt, ap);
- if (x < 0) x = COLS + x - (n - 1);
- if (y < 0) y = LINES + y;
- if (x + n > COLS) n = COLS - x;
- if (x < COLS && y < LINES && n > 0) {
- char buf[n + 1];
- vsnprintf(buf, n + 1, fmt, ap2);
- vui_putsna(x, y, buf, n, a);
- }
- va_end(ap2);
- return n;
-}
-
-int vui_aprintf(int x, int y, VuiAttr a, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- int r = vui_avprintf(x, y, a, fmt, ap);
- va_end(ap);
- return r;
-}
-
-int vui_printf(int x, int y, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- int r = vui_avprintf(x, y, ATTR_DEFAULT, fmt, ap);
- va_end(ap);
- return r;
-}
-
-int wait_for_input(int fd, int ms) {
- return poll(&(struct pollfd) { .fd = fd, .events = POLLIN }, 1, ms);
-}
-
-int has_input(void) {
- return wait_for_input(STDIN_FILENO, 0);
-}
-
-unsigned isqrt(unsigned x) {
- if (x <= 1) return x;
- unsigned a = x/2, b = (a + x/a) / 2;
- while (b < a) a = b, b = (a + x/a) / 2;
- return a;
-}
-
-void vui_scroll_buf(VuiBuffer *b, int dx, int dy) {
- if (abs(dx) >= b->width || abs(dy) >= b->height) {
- clear_buf(b);
- return;
- }
- if (dy > 0) {
- memmove(b->chr + (b->width * dy), b->chr, sizeof(*b->chr) * b->width * (b->height - dy));
- memmove(b->attr + (b->width * dy), b->attr, sizeof(*b->attr) * b->width * (b->height - dy));
- for (int y = 0; y < dy; y++) vui_clrtoeol(b, 0, y);
- } else if (dy < 0) {
- memmove(b->chr, b->chr + (b->width * -dy), sizeof(*b->chr) * b->width * (b->height + dy));
- memmove(b->attr, b->attr + (b->width * -dy), sizeof(*b->attr) * b->width * (b->height + dy));
- for (int y = b->height - 1; y > b->height + dy - 1; y--) vui_clrtoeol(b, 0, y);
- }
- if (dx > 0) {
- for (unsigned i = 0; i < b->height; i++) {
- memmove(b->chr + (i * b->width) + dx, b->chr + (i * b->width), sizeof(*b->chr) * (b->width - dx));
- memmove(b->attr + (i * b->width) + dx, b->attr + (i * b->width), sizeof(*b->attr) * (b->width - dx));
- vui_clrspan(b, 0, dx, i);
- }
- } else if (dx < 0) {
- for (unsigned i = 0; i < b->height; i++) {
- memmove(b->chr + (i * b->width), b->chr + (i * b->width) - dx, sizeof(*b->chr) * (b->width + dx));
- memmove(b->attr + (i * b->width), b->attr + (i * b->width) - dx, sizeof(*b->attr) * (b->width + dx));
- vui_clrspan(b, b->width + dx - 1, b->width, i);
- }
-
- }
-}
-
-void vui_scroll(int dx, int dy) {
- vui_scroll_buf(vui_win.front, dx, dy);
- vui_win.scroll_x += dx;
- vui_win.scroll_y += dy;
-}
-
-static unsigned getk(void) {
- char c;
- int r;
- do {
- r = read(STDIN_FILENO, &c, 1);
- } while (r == -1 && errno == EINTR);
- if (r == -1) return KEY_EOF;
- return c;
-}
-
-static unsigned getk_utf8(uint8_t start) {
- unsigned n = stdc_leading_ones(start);
- if (n > 4) return KEY_INVALID;
- static const uint8_t cpmask[4] = { 0x7f, 0x3f, 0x1f, 0xf };
- uint32_t ch = start & cpmask[n - 1];
- while (--n) {
- uint8_t k = getk();
- if ((k & 0xc0) != 0x80) return KEY_INVALID;
- ch = (ch << 6) | (k & 0x3f);
- }
- return ch;
-}
-
-VuiKey vui_bad_key(void) {
- /* yell */
- putchar('\a');
- fflush(stdout);
- return KEY_INVALID;
-}
-
-VuiKey vui_esc_key(u32 c);
-
-VuiKey vui_meta_key(u32 c) {
- switch (c) {
- case '2': return vui_esc_key(getk()) | KEY_SHIFT_BIT; break;
- case '3': return vui_esc_key(getk()) | KEY_ALT_BIT; break;
- case '4': return vui_esc_key(getk()) | KEY_ALT_BIT | KEY_SHIFT_BIT; break;
- case '5': return vui_esc_key(getk()) | KEY_CTRL_BIT; break;
- case '6': return vui_esc_key(getk()) | KEY_CTRL_BIT | KEY_SHIFT_BIT; break;
- case '7': return vui_esc_key(getk()) | KEY_CTRL_BIT | KEY_ALT_BIT; break;
- case '8': return vui_esc_key(getk()) | KEY_CTRL_BIT | KEY_SHIFT_BIT | KEY_ALT_BIT; break;
- case '9': return vui_esc_key(getk()) | KEY_META_BIT; break;
- case '1':
- switch (getk()) {
- case '0': return vui_esc_key(getk()) | KEY_META_BIT | KEY_SHIFT_BIT; break;
- case '1': return vui_esc_key(getk()) | KEY_META_BIT | KEY_ALT_BIT; break;
- case '3': return vui_esc_key(getk()) | KEY_META_BIT | KEY_CTRL_BIT; break;
- case '4': return vui_esc_key(getk()) | KEY_META_BIT | KEY_SHIFT_BIT | KEY_CTRL_BIT; break;
- case '5': return vui_esc_key(getk()) | KEY_META_BIT | KEY_ALT_BIT | KEY_CTRL_BIT; break;
- case '6': return vui_esc_key(getk()) | KEY_META_BIT | KEY_SHIFT_BIT | KEY_ALT_BIT | KEY_CTRL_BIT; break;
- default: return vui_bad_key();
- }
- default: return vui_bad_key();
- }
-}
-
-VuiKey vui_esc_key(u32 c) {
- switch (c) {
- case 'P': return KEY_F1; break;
- case 'Q': return KEY_F2; break;
- case 'R': return KEY_F3; break;
- case 'S': return KEY_F4; break;
- case '1':
- switch (getk()) {
- case ';': return vui_meta_key(getk());
- case '5':
- default: return vui_bad_key();
- }
- break;
- case 'A': return KEY_UP;
- case 'B': return KEY_DOWN;
- case 'C': return KEY_RIGHT;
- case 'D': return KEY_LEFT;
- case 'H': return KEY_HOME;
- case 'F': return KEY_END;
- case '5':
- if (getk() != '~') return vui_bad_key();
- return KEY_PGUP;
- case '6':
- if (getk() != '~') return vui_bad_key();
- return KEY_PGDN;
- default: return vui_bad_key();
- }
-}
-
-VuiKey vui_key(void) {
- int c = getk();
- if (c & 0x80) return getk_utf8(c);
- if (c == '\n') return KEY_RET;
- if (c != KEY_ESC) return c;
- if (!has_input()) return KEY_ESC;
- c = getk();
- switch (c) {
- case 'O':
- switch (getk()) {
- case 'P': return KEY_F1; break;
- case 'Q': return KEY_F2; break;
- case 'R': return KEY_F3; break;
- case 'S': return KEY_F4; break;
- default: return vui_bad_key(); break;
- }
- case '[': return vui_esc_key(getk());
- default: return vui_bad_key();
- }
-}
+#include "vui.h"
int x = 0, y = 0;
int dx = 0, dy = 0;
@@ -924,47 +8,9 @@ int C = '*';
unsigned frame = 0;
int half_y = 0;
-void draw(void *ctx) {
- (void)ctx;
-
- /*
- FILE *f = fopen("tmp.txt", "a");
- fprintf(f, "%u\n", vui_out_chars);
- fclose(f);
- */
-
- vui_blit();
-}
-
-uint64_t u64_hash(uint64_t key) {
- key ^= key >> 33;
- key *= 0xff51afd7ed558ccd;
- key ^= key >> 33;
- key *= 0xc4ceb9fe1a85ec53;
- key ^= key >> 33;
- return key;
-}
-
-int main(int argc, const char **argv) {
+int main(void) {
vui_init();
- /*
- for (;;) {
- VuiKey k = vui_key();
- printf("%X %u\n\r", k, UTF8_CP_LEN(k));
- printf("%016lX\n\r", UTF8_CP_LEN_BITS);
- printf("%016lX >> (%u * 2) = %016lX\n\r", UTF8_CP_LEN_BITS, stdc_leading_zeros((uint32_t)k), UTF8_CP_LEN_BITS >> (stdc_leading_zeros((uint32_t)k) << 1));
- printf("%016lX & 3 + 1 = %u\n\r", UTF8_CP_LEN_BITS >> (stdc_leading_zeros((uint32_t)k) << 1),
- ((UTF8_CP_LEN_BITS >> (stdc_leading_zeros((uint32_t)k) << 1)) & 3) + 1);
- printf(".\n\r");
- }
- vui_fini();
- return 0;
- */
-
- vui_curs_vis(1);
- vui_win.redraw_fn = draw;
-
int x = 0, y = 0;
int scroll_x = 0, scroll_y = 0;
for (;;) {
@@ -993,81 +39,22 @@ int main(int argc, const char **argv) {
scroll_x -= sdx;
scroll_y -= sdy;
- for (unsigned y = 0; y < LINES; y++) {
- for (unsigned x = 0; x < COLS; x++) {
- if (ATTR(x, y) == ATTR_DEFAULT || ATTR_FG(ATTR(x, y)) == ATTR_BG(ATTR(x, y)) ) {
- int sx = x + scroll_x;
- int sy = y + scroll_y;
- int okx = sx >= left - 5 && sx <= right + 5;
- int oky = sy >= top - 2 && sy <= bottom + 2;
- if (okx && oky) {
- ATTR(x, y) = FG_BLACK | BG_WHITE;
- } else if ((oky && sx == right + 6) || (okx && sy == bottom + 3) || (sx == right + 6 && sy == bottom + 3)) {
- ATTR(x, y) = FG_BLACK | BG_BLACK;
- } else {
- ATTR(x, y) = FG_BLUE | BG_BLUE;
- }
- }
- }
- }
-
- vui_curs_pos(x, y);
- draw(NULL);
+ vui_blit();
int animating = !!scroll_x || !!scroll_y;
- if (animating && !wait_for_input(STDIN_FILENO, 15)) continue;
+ if (animating && !vui_wait_for_input(50)) continue;
VuiKey c = vui_key();
if (c == KEY_INVALID || c == KEY_ESC) goto done;
- static unsigned i = 0;
- i++;
- if (c == KEY_RET) {
+ if (x >= right) {
x = left;
y += 2;
- } else if (x < right) {
- x += vui_aprintf(x, y, FG_BLACK | BG_WHITE, "%X ", c);
- if (c < 0x20 || c > KEY_UTF8_MAX || c == 0x7f) c = 0xFFFD;
- vui_chra(x, y, c, FG_BLACK | BG_WHITE);
- x += 2;
- }
-
- /*
- switch (c) {
- case KEY_ESC: goto done;
- case KEY_LEFT: x--; break;
- case KEY_RIGHT: x++; break;
- case KEY_UP: y--; break;
- case KEY_DOWN: y++; break;
- case KEY_HOME: x = 10; break;
- case KEY_END:
- x = COLS - 1;
- while (x > 9 && CHR(x, y) == ' ') x--;
- x++;
- break;
- case KEY_PGDN: y += 8; break;
- case KEY_PGUP: y -= 8; break;
- case KEY_RET: x = 10; y++; break;
- case KEY_DEL:
- case KEY_BACKSPACE:
- if (x > 0) x--;
- break;
- case KEY_INVALID:
- vui_fini();
- puts("invalid key!");
- return 1;
- default:
- if (c < 0x20 || c > KEY_UTF8_MAX) continue;
- {
- static unsigned a_ = 0;
- VuiAttr a = (a_++ >> 3) & ((1<<13)-1);
- if (ATTR_FG(a) == ATTR_BG(a)) {
- a = (a & ~0xf) | (~ATTR_FG(a) & 0xf);
- }
- vui_chr(x++, y, c);
- }
}
- */
+ static unsigned i = 0;
+ i++;
+ int tx = x + vui_aprintf(x, y - 1, (i & 1 ? FG_WHITE : FG_CYAN) | BG_BLACK, "%02X", c);
+ vui_chra(x++, y, c < 0x20 || c > KEY_UTF8_MAX || c == 0x7f ? 0xfffd : c, FG_BYELLOW | BG_BLUE);
+ while (x < tx) vui_chra(x++, y, ' ', FG_BYELLOW | BG_BLUE);
}
-
done:
vui_fini();