From d7479de95b8c4e8ada7db3676424fb0b2e1713cf Mon Sep 17 00:00:00 2001 From: WormHeamer Date: Thu, 6 Nov 2025 22:58:25 -0500 Subject: separate vui.c, vui.h --- main.c | 1033 +--------------------------------------------------------------- 1 file changed, 10 insertions(+), 1023 deletions(-) (limited to 'main.c') diff --git a/main.c b/main.c index b3503b8..54cfb9d 100644 --- a/main.c +++ b/main.c @@ -1,921 +1,5 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - #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(); -- cgit v1.2.3