From 6f84b07d939be6ef5e50c5468b95651fb4465500 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Fri, 12 Jun 2020 17:53:56 -0700 Subject: prototype of tinycurses --- sys/libterm/term.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 sys/libterm/term.c (limited to 'sys/libterm/term.c') diff --git a/sys/libterm/term.c b/sys/libterm/term.c new file mode 100644 index 0000000..cda2bbe --- /dev/null +++ b/sys/libterm/term.c @@ -0,0 +1,395 @@ +#include "term.h" + +struct ExtraInfo +{ + char *enteralt; + char *exitalt; + + char *entermouse; + char *exitmouse; +}; + +static +struct ExtraInfo vt200 = +{ + .enteralt = "\e[?1049h", + .exitalt = "\e[?1049l", + + .entermouse = "\e[?1049h\e[?1006l", + .exitmouse = "\e[?1002l\e[?1006l", +}; + +// ----------------------------------------------------------------------- +// database lookup + +static +char* +tryinfostr(Term *t, enum unibi_string s) +{ + char *val = (char*)unibi_get_str(t->info, s); + /* TODO: provide fallbacks */ + return val; +} + +static +char* +guessinfostr(Term *t, enum unibi_string s, char *guess) +{ + char *val = (char*)unibi_get_str(t->info, s); + if (!val) + return guess; + return val; +} + +static +char* +getinfostr(Term *t, enum unibi_string s) +{ + char *val = tryinfostr(t, s); + if (!val) + panicf("required term info string '%s' missing", unibi_name_str(s)); + + return val; +} + +static +char * +tryextrastr(Term *t, char *name) +{ + const char *nm; + size_t max = unibi_count_ext_str(t->info); + for (size_t i = 0; i < max; i++) { + nm = unibi_get_ext_str_name(t->info, i); + if (nm && !strcmp(nm, name)) { + return (char *)nm; + } + } + return nil; +} + +static +char * +guessextrastr(Term *t, char *name, char *guess) +{ + char *s; + if ((s = tryextrastr(t, name))) + return s; + + return guess; +} + +static +int +tryextrabool(Term *t, char *name) +{ + const char *nm; + size_t max = unibi_count_ext_bool(t->info); + for (size_t i = 0; i < max; i++) { + nm = unibi_get_ext_bool_name(t->info, i); + if (nm && !strcmp(nm, name)) { + return (int)i; + } + } + return -1; +} + +/* formats escape strings and writes to output */ +static void tfmt(Term *t, char *esc, int n, ...); + +// ----------------------------------------------------------------------- +// exported pen methods + +// ----------------------------------------------------------------------- +// exported term methods + +static +char * +ttmpbuf(Term *t, int len) +{ + if (t->tmp.len >= len) + return t->tmp.b; + + /* TODO: error handling */ + return (t->tmp.b = realloc(t->tmp.b, len)); +} + +void +tinit(Term *t) +{ + t->name = getenv("TERM"); + t->info = unibi_from_term(t->name); + if (!t->info) + panicf("could not identify terminal"); + + t->mode.mouse = 0; + t->mode.cursorvis = 1; + t->mode.altscreen = 0; + + t->cap.colors = unibi_get_num(t->info, unibi_max_colors); + t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase); + + /* TODO */ + t->input = nil; + t->root = nil; + t->pen = (Pen){0}; + + /* fill in buffers */ + t->buf.c = t->buf.b; + t->tmp.b = nil; + t->tmp.len = 0; + + /* get all term info format strings */ + t->esc.cup = getinfostr(t, unibi_cursor_address); + t->esc.vpa = tryinfostr(t, unibi_row_address); + t->esc.hpa = tryinfostr(t, unibi_column_address); + t->esc.cuu = getinfostr(t, unibi_parm_up_cursor); + t->esc.cuu1 = tryinfostr(t, unibi_cursor_up); + t->esc.cud = getinfostr(t, unibi_parm_down_cursor); + t->esc.cud1 = tryinfostr(t, unibi_cursor_down); + t->esc.cuf = getinfostr(t, unibi_parm_right_cursor); + t->esc.cuf1 = tryinfostr(t, unibi_cursor_right); + t->esc.cub = getinfostr(t, unibi_parm_left_cursor); + t->esc.cub1 = tryinfostr(t, unibi_cursor_left); + t->esc.ich = getinfostr(t, unibi_parm_ich); + t->esc.ich1 = tryinfostr(t, unibi_insert_character); + t->esc.dch = getinfostr(t, unibi_parm_dch); + t->esc.dch1 = tryinfostr(t, unibi_delete_character); + t->esc.il = getinfostr(t, unibi_parm_insert_line); + t->esc.il1 = tryinfostr(t, unibi_insert_line); + t->esc.dl = getinfostr(t, unibi_parm_delete_line); + t->esc.dl1 = tryinfostr(t, unibi_delete_line); + t->esc.ech = getinfostr(t, unibi_erase_chars); + t->esc.ed2 = getinfostr(t, unibi_clear_screen); + t->esc.stbm = getinfostr(t, unibi_change_scroll_region); + t->esc.sgr = getinfostr(t, unibi_set_attributes); + t->esc.sgr0 = getinfostr(t, unibi_exit_attribute_mode); + t->esc.sgr_i0 = tryinfostr(t, unibi_exit_italics_mode); + t->esc.sgr_i1 = tryinfostr(t, unibi_enter_italics_mode); + t->esc.sgr_fg = getinfostr(t, unibi_set_a_foreground); + t->esc.sgr_bg = getinfostr(t, unibi_set_a_background); + t->esc.sm_csr = getinfostr(t, unibi_cursor_normal); + t->esc.rm_csr = getinfostr(t, unibi_cursor_invisible); + + /* extensions to terminfo */ + t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); + t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); + + /* acs characters */ + t->acs.vline = tryinfostr(t, unibi_acs_vline); + t->acs.hline = tryinfostr(t, unibi_acs_hline); + t->acs.plus = tryinfostr(t, unibi_acs_plus); + t->acs.ltee = tryinfostr(t, unibi_acs_ltee); + t->acs.rtee = tryinfostr(t, unibi_acs_rtee); + t->acs.ttee = tryinfostr(t, unibi_acs_ttee); + t->acs.btee = tryinfostr(t, unibi_acs_btee); + t->acs.ulcorner = tryinfostr(t, unibi_acs_ulcorner); + t->acs.urcorner = tryinfostr(t, unibi_acs_urcorner); + t->acs.llcorner = tryinfostr(t, unibi_acs_llcorner); + t->acs.lrcorner = tryinfostr(t, unibi_acs_lrcorner); +} + +void +tfini(Term *t) +{ + if (t->mode.mouse) + twrite(t, 0, vt200.exitmouse); + if (!t->mode.cursorvis) + tfmt(t, t->esc.rm_csr, 0); + if (t->mode.altscreen) + twrite(t, 0, vt200.exitalt); + + tfmt(t, t->esc.sgr0, 0); +} + +void +tflush(Term *t) +{ + if (t->fd != -1) + write(t->fd, t->buf.b, t->buf.c - t->buf.b); + + t->buf.c = t->buf.b; +} + +void +twrite(Term *t, long len, char *s) +{ + int n; + if (!len) + len = strlen(s); + + while (len > 0) { + n = MIN(len, arrend(t->buf.b) - t->buf.c); + memcpy(t->buf.c, s, n); + t->buf.c += n; + len -= n; + tflush(t); + } +} + +void +tsetpen(Term *t, Pen new) +{ + int c; + ushort ic, in; + Pen cur = t->pen; + if (!memcmp(&new, &cur, sizeof(new))) + return; + + /* attributes */ + tfmt(t, t->esc.sgr, 9, + 0, /* standout */ + new.state & PenUnderline, + new.state & PenReverse, + new.state & PenBlink, + new.state & PenDim, + new.state & PenBold, + new.state & PenInvis, + 0, /* protect */ + 0); /* alt */ + + ic = cur.state & PenItalic; + in = new.state & PenItalic; + if (ic & ~in) + tfmt(t, t->esc.sgr_i0, 0); + else if (~ic & in) + tfmt(t, t->esc.sgr_i1, 0); + + /* fg/bg color */ + /* TODO: add a check for if the terminal supports true color */ + /* TODO: add a check for negative indices */ + if (new.state & PenTrueClr) { + tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b); + tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b); + } else { + tfmt(t, t->esc.sgr_fg, 1, new.col.fg); + tfmt(t, t->esc.sgr_bg, 1, new.col.bg); + } + + t->pen = new; +} + +static +void +tfmt(Term *t, char *esc, int n, ...) +{ + int i; + long len; + va_list args; + unibi_var_t param[9]; + char buf[64], *c = buf; + + if (!esc) + panicf("no terminfo escape string given"); + + va_start(args, n); + for (i = 0; i < arrlen(param) && i < n; i++) + param[i] = unibi_var_from_num(va_arg(args, int)); + va_end(args); + + len = unibi_run(esc, param, c, sizeof(buf)); + if (len < arrlen(buf)) { + c = ttmpbuf(t, len); + unibi_run(esc, param, c, len); + } + + twrite(t, len, c); +} + +/* absolute move */ +int +tgoto(Term *t, int row, int col) +{ + if (row != -1 && col != -1) + tfmt(t, t->esc.cup, 2, row, col); + else if (row != -1) { + if (!t->esc.vpa) + return 0; + tfmt(t, t->esc.vpa, 1, row); + } else if (col != -1) { + if (col == 0) { + twrite(t, 1, "\r"); + return 1; + } + if (t->esc.hpa) + tfmt(t, t->esc.hpa, 1, col); + else if (t->esc.cuf) { + twrite(t, 1, "\r"); + tfmt(t, t->esc.cuf, 1, col); + } else + return 0; + } else + return 0; /* unreachable */ + + return 1; +} + +/* relative move */ +void +tjump(Term *t, int down, int right) +{ + if (down == 1 && t->esc.cud1) + tfmt(t, t->esc.cud1, 0); + else if (down == -1 && t->esc.cuu1) + tfmt(t, t->esc.cuu1, 0); + else if (down > 0) + tfmt(t, t->esc.cud, 1, down); + else if (down < 0) + tfmt(t, t->esc.cuu, 1, -down); + + if (right == 1 && t->esc.cuf1) + tfmt(t, t->esc.cuf1, 0); + else if (right == -1 && t->esc.cub1) + tfmt (t, t->esc.cub1, 0); + else if (right > 0) + tfmt(t, t->esc.cuf, 1, right); + else if( right < 0) + tfmt(t, t->esc.cub, 1, -right); +} + +void +tdel(Term *t, int num) +{ + char *c, buf[64]; + if (num < 1) + return; + + /* TODO: allow for not moving */ + if (t->cap.bce) { + tfmt(t, t->esc.ech, 1, num); + tjump(t, 0, num); + } else { + c = buf; + memset(c, ' ', arrlen(buf)); + while(num > 64) { + twrite(t, arrlen(buf), c); + num -= arrlen(buf); + } + twrite(t, num, c); + } +} + +void +tclear(Term *t) +{ + tfmt(t, t->esc.ed2, 0); +} + +// ----------------------------------------------------------------------- +// testing entry + +int +main() +{ + Term t; + tinit(&t); + + printf("%s: %s\n", unibi_short_name_str(unibi_set_a_foreground), t.esc.sgr_fg); + printf("%s: %s\n", unibi_short_name_str(unibi_set_a_background), t.esc.sgr_bg); + tfmt(&t, t.esc.ext.rgbf, 3, 10, 10, 100); + tfmt(&t, t.esc.ext.rgbb, 3, 100, 0, 100); + twrite(&t, 0, "hello world\n"); + twrite(&t, 0, "hello world\n"); + twrite(&t, 0, t.acs.hline); + + tfini(&t); +} -- cgit v1.2.1