#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 */