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.h | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 sys/libterm/term.h (limited to 'sys/libterm/term.h') diff --git a/sys/libterm/term.h b/sys/libterm/term.h new file mode 100644 index 0000000..acae95f --- /dev/null +++ b/sys/libterm/term.h @@ -0,0 +1,489 @@ +#pragma once + +#include +#include + +#include +#include + +#define iota(x) 1 << (x) +/* + * obtained from: + * https://invisible-island.net/ncurses/man/curs_add_wch.3x.html + */ +#define ACSRUNES \ + /* name utf8 ascii acsc*/ \ + ACSRUNE("block", 0x25ae, '#', '0') \ + ACSRUNE("board", 0x2592, '#', 'h') \ + ACSRUNE("btee", 0x2534, '+', 'v') \ + ACSRUNE("bullet", 0x00b7, 'o', '~') \ + ACSRUNE("ckboard", 0x2592, ':', 'a') \ + ACSRUNE("darrow", 0x2193, 'v', '.') \ + ACSRUNE("degree", 0x00b0, '\'','f') \ + ACSRUNE("diamond", 0x25c6, '+', '`') \ + ACSRUNE("gequal", 0x2265, '>', '>') \ + ACSRUNE("hline", 0x2500, '-', 'q') \ + ACSRUNE("antern", 0x2603, '#', 'i') \ + ACSRUNE("larrow", 0x2190, '<', ',') \ + ACSRUNE("lequal", 0x2264, '<', 'y') \ + ACSRUNE("llcorner", 0x2514, '+', 'm') \ + ACSRUNE("lrcorner", 0x2518, '+', 'j') \ + ACSRUNE("ltee", 0x2524, '+', 't') \ + ACSRUNE("nequal", 0x2260, '!', '|') \ + ACSRUNE("pi", 0x03c0, '*', '{') \ + ACSRUNE("plminus", 0x00b1, '#', 'g') \ + ACSRUNE("plus", 0x253c, '+', 'n') \ + ACSRUNE("rarrow", 0x2192, '>', '+') \ + ACSRUNE("rtee", 0x251c, '+', 'u') \ + ACSRUNE("s1", 0x23ba, '-', 'o') \ + ACSRUNE("s3", 0x23bb, '-', 'p') \ + ACSRUNE("s7", 0x23bc, '-', 'r') \ + ACSRUNE("s9", 0x23bd, '_', 's') \ + ACSRUNE("sterling", 0x00a3, 'f', '}') \ + ACSRUNE("ttee", 0x252c, '+', 'w') \ + ACSRUNE("uarrow", 0x2191, '^', '-') \ + ACSRUNE("ulcorner", 0x250c, '+', 'l') \ + ACSRUNE("urcorner", 0x2510, '+', 'k') \ + ACSRUNE("vline", 0x2502, '|', 'x') \ + /* thick versions */ \ + ACSRUNE("t_btee", 0x253b, '+', 'V') \ + ACSRUNE("t_hline", 0x2501, '-', 'Q') \ + ACSRUNE("t_llcorner", 0x2517, '+', 'M') \ + ACSRUNE("t_lrcorner", 0x251b, '+', 'J') \ + ACSRUNE("t_ltee", 0x252b, '+', 'T') \ + ACSRUNE("t_plus", 0x254b, '+', 'N') \ + ACSRUNE("t_rtee", 0x2523, '+', 'U') \ + ACSRUNE("t_ttee", 0x2533, '+', 'W') \ + ACSRUNE("t_ulcorner", 0x250f, '+', 'L') \ + ACSRUNE("t_urcorner", 0x2513, '+', 'K') \ + ACSRUNE("t_vline", 0x2503, '|', 'X') \ + /* double version */ \ + ACSRUNE("d_btee", 0x2569, '+', 'H') \ + ACSRUNE("d_hline", 0x2550, '-', 'R') \ + ACSRUNE("d_llcorner", 0x255a, '+', 'D') \ + ACSRUNE("d_lrcorner", 0x255d, '+', 'A') \ + ACSRUNE("d_ltee", 0x2560, '+', 'F') \ + ACSRUNE("d_plus", 0x256c, '+', 'E') \ + ACSRUNE("d_rtee", 0x2563, '+', 'G') \ + ACSRUNE("d_ttee", 0x2566, '+', 'I') \ + ACSRUNE("d_ulcorner", 0x2554, '+', 'C') \ + ACSRUNE("d_urcorner", 0x2557, '+', 'B') \ + ACSRUNE("d_vline", 0x2551, '|', 'Y') + +/* enums */ + +/* key symbols */ +enum +{ + SymUnknown = -1, + SymNone = 0, + + /* special names in c0 */ + SymBackspace, SymTab, SymEnter, SymEscape, + + /* special names in g0 */ + SymSpace, SymDel, + + /* special keys */ + SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert, + SymDelete, SymSelect, SymPageup, SymPagedown, SymHome, SymEnd, + + /* special keys from terminfo */ + SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit, + SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions, + SymPrint, SymRedo, SymReference, SymRefresh, SymReplace, + SymRestart, SymResume, SymSave, SymSuspend, SymUndo, + + /* numeric keypad special keys */ + SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8, + SymKp9, SymKpenter, SymKpplus, SymKpminus, SymKpmult, SymKpdiv, SymKpcomma, + SymKpperiod, SymKpequals, + + /* et cetera ad nauseum */ + NumSyms +}; + +/* key type */ +enum +{ + KeyUnicode, + KeyFunc, + KeySym, + KeyMouse, + KeyPosition, + KeyModeReport, + KeyDCS, + KeyOSC, + /* add other recognised types here */ + + KeyUnknownCSI = -1 +}; + +/* key events */ +enum KeyEvent +{ + EvNil, + EvKey, + EvEOF, + EvAgain, + EvErr, +}; + +enum MouseEvent +{ + MouseNil, + MousePress, + MouseDrag, + MouseRelease, +}; + +enum +{ + ModShift = iota(0), + ModAlt = iota(1), + ModCtrl = iota(2), +}; + +enum +{ + FlagNoInterpret = iota(0), + FlagConvertKP = iota(1), + FlagRaw = iota(2), + FlagUTF8 = iota(3), + FlagNoTermIOS = iota(4), + FlagSpaceSymbol = iota(5), + FlagCtrlC = iota(6), + FlagEintr = iota(7), +}; + +enum +{ + HarmonizeSpace = iota(0), + HarmonizeDelBS = iota(1), +}; + +/* types */ +typedef struct RGB8 RGB8; +typedef struct Pen Pen; + +typedef struct Cell Cell; +typedef struct Row Row; +typedef struct Buffer Buffer; +typedef struct Window Window; + +typedef struct Node Node; +typedef struct Key Key; +typedef struct Input Input; + +typedef struct Term Term; + +struct RGB8 +{ + uint8 r, g, b; +}; + +enum +{ + PenNormal = 0, + PenBold = iota(0), + PenDim = iota(1), + PenInvis = iota(2), + PenItalic = iota(3), + PenReverse = iota(4), + PenStrike = iota(5), + PenUnderline = iota(6), + PenBlink = iota(7), + /* ... */ + PenTrueClr = iota(15), +}; + +struct Pen +{ + ushort state; + union { + /* 256 color (legacy) */ + struct { + sshort fg : 8, bg : 8; /* 0 - 255 or COLOUR_DEFAULT */ + } col; + /* true color (modern) */ + struct { + RGB8 fg, bg; + } rgb; + }; +}; + +/* outputs */ +struct Cell { + rune r; + Pen pen; +}; + +struct Row { + Cell *cells; + uint dirty:1; +}; + +/* Buffer holding the current window content (as an array) as well + * as the scroll back buffer content (as a circular/ring buffer). + * + * If new content is added to terminal the view port slides down and the + * previously top most line is moved into the scroll back buffer at postion + * scroll.index. This index will eventually wrap around and thus overwrite + * the oldest lines. + * + * In the scenerio below a scroll up has been performed. That is 'scroll.above' + * lines still lie above the current view port. Further scrolling up will show + * them. Similarly 'scroll.below' is the amount of lines below the current + * viewport. + * + * The function buffer_boundary sets the row pointers to the start/end range + * of the section delimiting the region before/after the viewport. The functions + * buffer_row_{first,last} return the first/last logical row. And + * buffer_row_{next,prev} allows to iterate over the logical lines in either + * direction. + * + * scroll back buffer + * + * scroll_buf->+----------------+-----+ + * | | | ^ \ + * | before | | | | + * current terminal content | viewport | | | | + * | | | | + * +----------------+-----+\ | | | s > scroll.above + * ^ | | i | \ | | i | c | + * | | | n | \ | | n | r | + * | | v | \ | | v | o | + * r | | i | \ | | i | l / + * o | viewport | s | >|<- scroll.index | s | l \ + * w | | i | / | | i | | + * s | | b | / | after | b | s > scroll.below + * | | l | / | viewport | l | i | + * v | | e | / | | e | z / + * +----------------+-----+/ | unused | | e + * <- maxcols -> | scroll back | | + * <- cols -> | buffer | | | + * | | | | + * | | | v + * roll_buf + scroll.size->+----------------+-----+ + * <- maxcols -> + * <- cols -> + */ + +struct Buffer { + Row *row; /* array of Row pointers of size 'rows' */ + Row *crow; /* row on which the cursor currently resides */ + bool *tabs; /* a boolean flag for each column whether it is a tab */ + struct { + Row *buf; /* a ring buffer holding the scroll back content */ + Row *top; /* row in lines where scrolling region starts */ + Row *bot; /* row in lines where scrolling region ends */ + int size; /* maximal capacity of scroll back buffer (in lines) */ + int index; /* current index into the ring buffer */ + int above; /* number of lines above current viewport */ + int below; /* number of lines below current viewport */ + } scroll; + Pen pen, spen; + int nrow, ncol; /* current dimension of buffer */ + int maxcols; /* allocated cells (maximal cols over time) */ + int srow, scol; /* saved cursor row/column (zero based) */ +}; + +/* input */ +struct Key +{ + int type; + int mods; + uchar utf8[UTFmax+1]; + union { + rune pt; + int num; + int sym; + char mouse[4]; + } code; +}; + +struct KeyInfo +{ + int type; + int sym; + int modmask; + int modset; +}; + +/* make opaque? */ +struct Input +{ + int fd; + int flags; + int hflag; + int wait; /* in ms */ + + struct termios oldterm; + + /* buffer */ + struct { + long off; + uchar *b, *c, *e, bytes[256]; + } buf; + + /* modifiers */ + char closed : 1; + char started : 1; + char hasold : 1; + + /* key data */ + Node *keys; + struct KeyInfo c0[32]; + + char **keynm; + int nkeynm; + + int nsavedcsi; + char *savedcsi; +}; + +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 */ + Window *root; + Pen pen; + + /* 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 + + /* augmentations to terminfo */ + struct { + char *rgbf; // rgb foreground + char *rgbb; // rgb background + char *smxx; // strikethrough + char *smulx; // curly underline + } ext; + } esc; + + /* basic shapes */ + struct { + rune block; + rune board; + rune hline; + rune vline; + rune plus; + rune ltee; + rune rtee; + rune ttee; + rune btee; + rune ulcorner; + rune urcorner; + rune llcorner; + rune lrcorner; + } acs; +}; + +/* + * exported functions + */ + +#if 0 +/* buffer.c */ +void zero(Row *row, int start, int len); +void roll(Row *start, Row *end, int count); + +void bclear(Buffer *b); +void bfree(Buffer *b); +void bscroll(Buffer *b, int s); +void bresize(Buffer *b, int rows, int cols); +bool binit(Buffer *b, int rows, int cols, int size); +void brender(Buffer *b, Term *t); +void bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) ; +Row *browfirst(Buffer *b); +Row *browlast(Buffer *b); +Row *brownext(Buffer *b, Row *row); +Row *bprevrow(Buffer *b, Row *row); + +/* input.c */ +Input *makeinput(int fd, int flags, unibi_term *info); +void freeinput(Input *in); +int startrecord(Input *in); +int stoprecord(Input *in); +char *keyname(Input *in, int sym); + +enum KeyEvent term·waitkey(Input *in, Key *key); /* block until next keypress */ +enum KeyEvent term·getkey(Input *in, Key *key); /* grab key if we can */ +enum KeyEvent term·demandkey(Input *in, Key *key); /* grab now and interpret as best we can */ + +/* unpack key event into useful data */ +enum KeyEvent decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col); +enum KeyEvent decodepos(Input *in, Key *key, int *row, int *col); +enum KeyEvent decodemode(Input *in, Key *key, int *init, int *mode, int *val); +#endif + +/* term.c */ +void term·init(Term *t); +void term·fini(Term *t); +void term·flush(Term *t); +void term·write(Term *t, long len, char *s); +int term·goto(Term *t, int row, int col); +void term·jump(Term *t, int down, int right); +void term·del(Term *t, int num); +void term·setpen(Term *t, Pen pen); +void term·clear(Term *t); + +/* input.c */ +enum KeyEvent term·waitkey(Term *t, Key *key); /* block until next keypress */ +enum KeyEvent term·getkey(Term *t, Key *key); /* grab key if we can */ +enum KeyEvent term·demandkey(Term *t, Key *key); /* grab now and interpret as best we can */ -- cgit v1.2.1