diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 355 |
1 files changed, 222 insertions, 133 deletions
@@ -5,6 +5,7 @@ #include <assert.h> #include <stdarg.h> #include <stdbit.h> +#include <errno.h> #include <stdio.h> #include <unistd.h> @@ -14,6 +15,8 @@ #include <poll.h> #include <time.h> +#include "wrmr.h" + #define CSI "\x1b[" /* TODO @@ -25,6 +28,24 @@ */ 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_INVALID = 0x7fffffff +} VuiKey; + +typedef enum { FG_BLACK = 0, FG_RED = 1, FG_GREEN = 2, @@ -74,9 +95,11 @@ typedef enum { #define ATTR_A(a) ((a) & ~0xff) #define ATTR_DEFAULT (FG_WHITE | BG_BLACK) +typedef uint32_t VuiChar; + typedef struct { unsigned width, height; - char *chr; + VuiChar *chr; uint16_t *attr; } VuiBuffer; @@ -88,6 +111,7 @@ typedef struct { 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; @@ -135,7 +159,7 @@ static void vui_clear(void) { } static void resize_buf(VuiBuffer *buf, unsigned nw, unsigned nh) { - char *nchr = calloc(nw * nh, sizeof(*buf->chr)); + VuiChar *nchr = calloc(nw * nh, sizeof(*buf->chr)); uint16_t *nattr = calloc(nw * nh, sizeof(*buf->attr)); assert(nchr); assert(nattr); @@ -176,6 +200,7 @@ static void vui_adjust(void) { } void vui_curs_vis(int vis) { + vui_win.curs_vis = vis; if (vis) { fputs(CSI "?25h", stdout); } else { @@ -183,6 +208,11 @@ void vui_curs_vis(int vis) { } } +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) { @@ -203,6 +233,8 @@ void vui_init(void) { /* 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); @@ -235,7 +267,7 @@ void vui_fini(void) { free(vui_out); vui_out = NULL; vui_out_cap = 0; - printf(CSI "H" CSI "2J" CSI "0m"); + // printf(CSI "H" CSI "2J" CSI "0m"); } void vui_out_fit(size_t n) { @@ -264,11 +296,64 @@ void vui_outf(const char *fmt, ...) { 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)]. + * + * 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: + * lut = 0x156AAFFFFFFFF + * len(cp) = 1 + (lut >> (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() */ +void vui_outvc(VuiChar c) { + ASSUME(c > 0 && c <= 0x10ffff); + 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); @@ -282,7 +367,7 @@ static inline void vui_outs(const char *s) { static inline void vui_out_flush(void) { fwrite(vui_out, 1, vui_outn, stdout); fflush(stdout); -#if 0 +#if 1 static unsigned out_frame = 0; FILE *f = fopen("out_log.txt", "a"); assert(f); @@ -387,7 +472,8 @@ static void attr_chg(VuiAttr *ptr, VuiAttr to) { if ((to ^ from) & A_REVERSE) M((to & A_REVERSE) ? "7" : "27"); } -chg_colors: +chg_colors:; + int f_fg = ATTR_FG(from); int f_bg = ATTR_BG(from); int t_fg = ATTR_FG(to); @@ -447,29 +533,21 @@ void vui_blit(void) { //vui_win.redraw_all = 1; if (vui_win.redraw_all) { - vui_outs(CSI "H" CSI "0m"); + 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_outc(front->chr[i]); + vui_outvc(front->chr[i]); } vui_outs(CSI "H"); cur_x = 0; cur_y = 0; - /* - for (unsigned y = 0; y < LINES; y++) { - for (unsigned x = 0; x < COLS; x++) { - attr_chg(&attr_last, ATTR(x, y)); - vui_outc(CHR(x, y)); - } - } - */ vui_win.redraw_all = 0; vui_win.scroll_x = 0; vui_win.scroll_y = 0; - goto copy_buf; + goto end; } vui_changes = 0; @@ -531,14 +609,18 @@ scrolled: vui_changes++; curs_move(&cur_x, &cur_y, x0, y); attr_chg(&attr_last, a); - vui_outsn(&CHR(x0, y), x - x0); + vui_outvcn(&CHR(x0, y), x - x0); cur_x = x; if (x - x0 > vui_max_change) vui_max_change = x - x0; } } } -copy_buf: +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)); @@ -547,14 +629,14 @@ copy_buf: vui_win.back = front; } -void vui_chra(int x, int y, char c, VuiAttr a) { +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, char c) { +void vui_chr(int x, int y, VuiChar c) { vui_chra(x, y, c, ATTR_DEFAULT); } @@ -596,6 +678,10 @@ 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; @@ -639,6 +725,57 @@ void vui_scroll(int dx, int dy) { 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_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(); + if (c != '[') goto bad_esc_seq; + switch (getk()) { + 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() != '~') goto bad_esc_seq; + return KEY_PGUP; + case '6': + if (getk() != '~') goto bad_esc_seq; + return KEY_PGDN; + default: goto bad_esc_seq; + } +bad_esc_seq: + putchar('\a'); + return KEY_INVALID; +} + int x = 0, y = 0; int dx = 0, dy = 0; int camx = 0, camy = 0; @@ -649,134 +786,86 @@ int half_y = 0; void draw(void *ctx) { (void)ctx; - vui_chra(x, y, C, ((x + y) & 0xf) | BG_BBLUE); - vui_chra(x-1, y, C, ((x + y) & 0xf) | BG_BLUE | A_UNDERSCORE); - vui_chra(x+1, y, C, ((x + y) & 0xf) | BG_BLUE | A_UNDERSCORE); - vui_chra(x, y-1, C, ((x + y) & 0xf) | BG_BLUE | A_UNDERSCORE); - vui_chra(x, y+1, C, ((x + y) & 0xf) | BG_BLUE | A_UNDERSCORE); - - vui_printf(-1, -1, "(%u, %u)", COLS, LINES); - vui_printf(0, -1, "scrolled (%d, %d)", scrolled_x, scrolled_y); - vui_printf(-1, 0, "longest change = %3u, changes = %5u, output = %5u, chars = %5u", vui_max_change, vui_changes, vui_out_chars, COLS * LINES); + 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) { vui_init(); - vui_curs_vis(0); + /* for (;;) { - frame++; - x += dx; - int ydy = dy; - if (half_y) ydy = (frame & 1) ? (dy / 2) : dy - (dy / 2); - y += ydy; - - int left = 32; - int right = COLS - (left + 1); - int top = left >> half_y; - int bottom = LINES - (top + 1); - - int lcamx = camx, lcamy = camy; - if (x < left) { camx += x - left; x = left; } - if (x > right) { camx += x - right; x = right; } - if (y < top) { camy += y - top; y = top; } - if (y > bottom) { camy += y - bottom; y = bottom; } - - /* - if (x < 0) x += win.width; - if (x > win.width) x -= win.width; - if (y < 0) y += win.height; - if (y > win.height) y -= win.height; - */ - - static int paused = 0; - vui_win.redraw_fn = draw; - -#if 1 - /* weird cellular automata */ - if (!paused) { - int vsteps = (COLS * LINES) / 2048; - if (!vsteps) vsteps++; - vsteps = 1; - int y0 = LINES * (frame % vsteps) / vsteps; - int y1 = y0 + LINES / vsteps; - if (y1 > LINES) y1 = LINES; - for (int y = y0; y < y1; y++) { - for (int x = 0; x < COLS; x++) { - if (frame & 1) { - int txa = x, tya = y, txc = x, tyc = y; - txa += (random() % 3) - 1; - tya += (random() % 3) - 1; - txc += (random() % 3) - 1; - tyc += (random() % 3) - 1; - if (txa >= 0 && txa < COLS && tya >= 0 && tya < LINES) { - ATTR(txa, tya) = (BATTR(vui_win.back,x,y) & ~0xf) | (ATTR(txa,tya) & 0xf); - if (!(random() & 127)) ATTR(x,y) = ATTR_DEFAULT; - } - if (txc >= 0 && txc < COLS && tyc >= 0 && tyc < LINES) { - CHR(txc, tyc) = BCHR(vui_win.back, x, y); - ATTR(txc, tyc) = (ATTR(txc,tyc) & ~0xf) | (BATTR(vui_win.back,x,y) & 0xf); - if (!(random() & 127)) CHR(x,y) = ' '; - } - } - } - } + 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; + */ - for (unsigned i = 0; i < isqrt(COLS * LINES) / 10; i++) { - int tx = random() % COLS; - int ty = random() % LINES; - // VuiAttr a = ((random()&15) << 4) | (random()&15); - char ch = ' ' + (random() % 0x5f); - VuiAttr a = random() & ((1 << 13) - 1); - vui_chra(tx, ty, ch, a); - } - } + vui_curs_vis(1); + vui_win.redraw_fn = draw; -#else - vui_clear(); - for (unsigned y = 0; y < LINES; y++) { - for (unsigned x = 0; x < COLS; x++) { - int tx = x + camx, ty = y + camy; - char ch = " ',."[(tx^ty)&3]; - /* int abp = (((tx>>4)^(ty>>4)) % 3 == 0); - VuiAttr aa = abp ? BG_CYAN : BG_WHITE; - VuiAttr ab = abp ? BG_BLUE : BG_RED; */ - unsigned abp = (tx >> 4) ^ (ty >> 4); - VuiAttr aa = (abp & 15) << 4; - VuiAttr ab = ((abp+1) & 15) << 4; - VuiAttr a = (((tx>>2)^(ty>>2)) % 3 == 0) ? aa : ab; - vui_chra(x, y, ch, a); - } - } -#endif + int x = 0, y = 0; + for (;;) { + if (x < 10) { vui_scroll(10 - x, 0); x = 10; } + if (y < 5) { vui_scroll(0, 5 - y); y = 5; } + if (x > COLS-10) { vui_scroll((COLS-10) - x, 0); x = COLS-10; } + if (y > LINES-5) { vui_scroll(0, (LINES-5) - y); y = LINES-5; } - int dcx = camx - lcamx, dcy = camy - lcamy; + vui_curs_pos(x, y); draw(NULL); - vui_scroll(-dcx, -dcy); - - clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) { .tv_nsec = 15 * 1000000 }, NULL); - if (!wait_for_input(STDIN_FILENO, 0)) continue; - int c = getchar(); - - if (c == 'q') break; - if (c > 0x20 && c < 0x7f) C = c; + VuiKey c = vui_key(); switch (c) { - /* - case 'j': dx=-1; dy=0; break; - case 'i': dx=0; dy=-1; break; - case 'k': dx=0; dy=1; break; - case 'l': dx=1; dy=0; break; - */ - case 'j': dx--; break; - case 'l': dx++; break; - case 'i': dy--; break; - case 'k': dy++; break; - case ' ': paused = !paused; break; + 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_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) 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); + } } } + +done: + vui_fini(); - printf(CSI "2J" CSI "H %u, %u\n", COLS, LINES); return 0; } |
