summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c355
1 files changed, 222 insertions, 133 deletions
diff --git a/main.c b/main.c
index c25a174..f14684e 100644
--- a/main.c
+++ b/main.c
@@ -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;
}