#include #include #include typedef struct RGB8 RGB8; typedef struct Pen Pen; typedef struct Term Term; 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", }; struct RGB8 { uint8 r, g, b; }; struct Pen { uint bold : 1; uint italic : 1; uint reverse : 1; uint strike : 1; uint blink : 1; /* colors */ uint color : 3; union { /* 256 color (legacy) */ struct { sint fg : 9, bg : 9; /* 0 - 255 or COLOUR_DEFAULT */ } idx; /* true color (modern) */ struct { RGB8 fg, bg; } rgb; }; }; struct Term { /* meta data */ char *name; unibi_term *info; struct { uint altscreen : 1; uint cursorvis : 1; uint mouse : 1; } mode; struct { uint bce : 1; int colors; } cap; /* input capture */ // Input *input; /* output display */ int nrow, ncol; /* output raw text */ int fd; struct { char *c, b[512]; } buf; struct { int len; char *b; } tmp; /* info */ struct { /* Positioning */ char *cup; // cursor_address char *vpa; // row_address == vertical position absolute char *hpa; // column_address = horizontal position absolute /* Moving */ char *cuu; char *cuu1; // Cursor Up char *cud; char *cud1; // Cursor Down char *cuf; char *cuf1; // Cursor Forward == Right char *cub; char *cub1; // Cursor Backward == Left /* Editing */ char *ich; char *ich1; // Insert Character char *dch; char *dch1; // Delete Character char *il; char *il1; // Insert Line char *dl; char *dl1; // Delete Line char *ech; // Erase Character char *ed2; // Erase Data 2 == Clear screen char *stbm; // Set Top/Bottom Margins /* formatting */ char *sgr; // Select Graphic Rendition char *sgr0; // Exit Attribute Mode char *sgr_i0, *sgr_i1; // SGR italic off/on char *sgr_fg; // SGR foreground colour char *sgr_bg; // SGR background colour // Mode setting/clearing char *sm_csr; char *rm_csr; // Set/reset mode: Cursor visible } esc; }; // ----------------------------------------------------------------------- // 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 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); } } 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); } // ----------------------------------------------------------------------- // 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* 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; } int main() { Term t; t.name = getenv("TERM"); t.info = unibi_from_term(t.name); t.mode.mouse = 0; t.mode.cursorvis = 1; t.mode.altscreen = 0; if (!t.info) panicf("could not identify terminal"); 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); }