#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wrmr.h" #define CSI "\x1b[" /* TODO * * - utf-8 input * - arrow keys * - KEY_RESIZE or draw callback * - 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_INVALID = 0x7fffffff } 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); } 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); 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) { tcsetattr(STDIN_FILENO, TCSANOW, &vui_init_stdin_tos); tcsetattr(STDOUT_FILENO, TCSANOW, &vui_init_stdout_tos); 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 "H" CSI "2J" CSI "0m"); } 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)]. * * 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); 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 1 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 sep = 0; 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); sep = 1; } else { 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); } 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 >= COLS || y >= LINES) return n; if (n > 0) { char buf[n + 1]; vsnprintf(buf, n + 1, fmt, ap2); memcpy(&CHR(x,y), buf, n < COLS - x ? n : COLS - x); for (unsigned x1 = x; x1 < COLS && x1 < x + n; x1++) { ATTR(x1, y) = a; } } 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_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; 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) { 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; 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; } vui_curs_pos(x, y); draw(NULL); VuiKey c = vui_key(); 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_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(); return 0; }