From e41eb5691417ecacade402759231f64778e3147f Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Wed, 10 Jun 2020 14:49:45 -0700 Subject: checkin: massive restructuring of dvtm --- sys/cmd/dvtm/buffer.c | 304 ++++++++ sys/cmd/dvtm/buffer.h | 0 sys/cmd/dvtm/config.h | 208 ++++++ sys/cmd/dvtm/driver.c | 335 +++++++++ sys/cmd/dvtm/dvtm.c | 161 +++-- sys/cmd/dvtm/dvtm.h | 117 ++-- sys/cmd/dvtm/events.c | 1840 +++++++++++++++++++++++++++++++++++++++++++++++++ sys/cmd/dvtm/rules.mk | 10 +- sys/cmd/dvtm/term.c | 0 sys/cmd/dvtm/term.h | 137 ++++ sys/cmd/dvtm/vt.c | 1258 +++++++++------------------------ sys/cmd/dvtm/vt.h | 12 +- sys/cmd/dvtm/window.c | 42 ++ sys/cmd/dwm/config.h | 12 +- sys/cmd/dwm/dwm.h | 1 + sys/cmd/rules.mk | 4 +- sys/cmd/term/term.h | 1 - 17 files changed, 3406 insertions(+), 1036 deletions(-) create mode 100644 sys/cmd/dvtm/buffer.c create mode 100644 sys/cmd/dvtm/buffer.h create mode 100644 sys/cmd/dvtm/config.h create mode 100644 sys/cmd/dvtm/driver.c create mode 100644 sys/cmd/dvtm/events.c create mode 100644 sys/cmd/dvtm/term.c create mode 100644 sys/cmd/dvtm/term.h create mode 100644 sys/cmd/dvtm/window.c (limited to 'sys/cmd') diff --git a/sys/cmd/dvtm/buffer.c b/sys/cmd/dvtm/buffer.c new file mode 100644 index 0000000..f7e73c0 --- /dev/null +++ b/sys/cmd/dvtm/buffer.c @@ -0,0 +1,304 @@ +#include "buffer.h" + +/* row operations */ +void +zero(Row *row, int start, int len) +{ + int i; + Cell cell = { + .r = L'\0', + .pen = {0}; + }; + + for (i = start; i < len + start; i++) + row->cells[i] = cell; + row->dirty = true; +} + +void +roll(Row *start, Row *end, int count) +{ + int n = end - start; + + count %= n; + if (count < 0) + count += n; + + if (count) { + char buf[count * sizeof(Row)]; + memcpy(buf, start, count * sizeof(Row)); + memmove(start, start + count, (n - count) * sizeof(Row)); + memcpy(end - count, buf, count * sizeof(Row)); + for (Row *row = start; row < end; row++) + row->dirty = true; + } +} + +/* buffer operations */ +void +bclear(Buffer *b) +{ + int i; + Cell cell = { + .r = L'\0', + .pen = { + .state = PenNormal, + .col.fg = -1, + .col.bg = -1, + }, + }; + + for (i = 0; i < b->rows; i++) { + Row *row = b->lines + i; + for (int j = 0; j < b->cols; j++) { + row->cells[j] = cell; + row->dirty = true; + } + } +} + +void +bfree(Buffer *b) +{ + int i; + + for (i = 0; i < b->rows; i++) + free(b->lines[i].cells); + + free(b->lines); + + for (i = 0; i < b->scroll.size; i++) + free(b->scroll.buf[i].cells); + + free(b->scroll.buf); + free(b->tabs); +} + +void +bscroll(Buffer *b, int s) +{ + /* work in screenfuls */ + int ssz = b->scroll.bot - b->scroll.top; + + if (s > ssz) { + bscroll(b, ssz); + bscroll(b, s - ssz); + return; + } + if (s < -ssz) { + bscroll(b, -ssz); + bscroll(b, s + ssz); + return; + } + + b->scroll.above += s; + if (b->scroll.above >= b->scroll.size) + b->scroll.above = b->scroll.size; + + if (s > 0 && b->scroll.size) { + for (int i = 0; i < s; i++) { + Row tmp = b->scroll.top[i]; + b->scroll.top[i] = b->scroll.buf[b->scroll.index]; + b->scroll.buf[b->scroll.index] = tmp; + + b->scroll.index++; + if (b->scroll.index == b->scroll.size) + b->scroll.index = 0; + } + } + roll(b->scroll.top, b->scroll.bot, s); + if (s < 0 && b->scroll.size) { + for (int i = (-s) - 1; i >= 0; i--) { + b->scroll.index--; + if (b->scroll.index == -1) + b->scroll.index = b->scroll.size - 1; + + Row tmp = b->scroll.top[i]; + b->scroll.top[i] = b->scroll.buf[b->scroll.index]; + b->scroll.buf[b->scroll.index] = tmp; + b->scroll.top[i].dirty = true; + } + } +} + +void +bresize(Buffer *b, int rows, int cols) +{ + Row *lines = b->lines; + + if (b->rows != rows) { + if (b->crow >= lines + rows) { + /* scroll up instead of simply chopping off bottom */ + bscroll(b, (b->crow - b->lines) - rows + 1); + } + while (b->rows > rows) { + free(lines[b->rows - 1].cells); + b->rows--; + } + + lines = realloc(lines, sizeof(Row) * rows); + } + + if (b->maxcols < cols) { + for (int row = 0; row < b->rows; row++) { + lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols); + if (b->cols < cols) + zero(lines + row, b->cols, cols - b->cols); + lines[row].dirty = true; + } + Row *sbuf = b->scroll.buf; + for (int row = 0; row < b->scroll.size; row++) { + sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols); + if (b->cols < cols) + zero(sbuf + row, b->cols, cols - b->cols); + } + b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols); + for (int col = b->cols; col < cols; col++) + b->tabs[col] = !(col & 7); + b->maxcols = cols; + b->cols = cols; + } else if (b->cols != cols) { + for (int row = 0; row < b->rows; row++) + lines[row].dirty = true; + b->cols = cols; + } + + int deltarows = 0; + if (b->rows < rows) { + while (b->rows < rows) { + lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell)); + zero(lines + b->rows, 0, b->maxcols); + b->rows++; + } + + /* prepare for backfill */ + if (b->crow >= b->scroll.bot - 1) { + deltarows = b->lines + rows - b->crow - 1; + if (deltarows > b->scroll.above) + deltarows = b->scroll.above; + } + } + + b->crow += lines - b->lines; + b->scroll.top = lines; + b->scroll.bot = lines + rows; + b->lines = lines; + + /* perform backfill */ + if (deltarows > 0) { + bscroll(b, -deltarows); + b->crow += deltarows; + } +} + +bool +binit(Buffer *b, int rows, int cols, int size) +{ + b->pen.state = PenNormal; + b->pen.col.fg = b->pen.col.fg = -1; + + if (size < 0) + size = 0; + if (size && !(b->scroll.buf = calloc(size, sizeof(Row)))) + return false; + + b->scroll.size = size; + bresize(b, rows, cols); + return true; +} + +void +bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) +{ + if (bs) + *bs = nil; + if (be) + *be = nil; + if (as) + *as = nil; + if (ae) + *ae = nil; + if (!b->scroll.size) + return; + + if (b->scroll.above) { + if (bs) + *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size]; + if (be) + *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size]; + } + if (b->scroll.below) { + if (as) + *as = &b->scroll.buf[b->scroll.index]; + if (ae) + *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size]; + } +} + +Row * +browfirst(Buffer *b) +{ + Row *bstart; + if (!b->scroll.size || !b->scroll.above) + return b->lines; + bboundary(b, &bstart, nil, nil, nil); + return bstart; +} + +Row * +browlast(Buffer *b) +{ + Row *aend; + if (!b->scroll.size || !b->scroll.below) + return b->lines + b->rows - 1; + bboundary(b, nil, nil, nil, &aend); + return aend; +} + +Row * +brownext(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->lines, *last = b->lines + b->rows - 1; + + if (!row) + return nil; + + bboundary(b, &before_start, &before_end, &after_start, &after_end); + + if (row >= first && row < last) + return ++row; + if (row == last) + return after_start; + if (row == before_end) + return first; + if (row == after_end) + return nil; + if (row == &b->scroll.buf[b->scroll.size - 1]) + return b->scroll.buf; + return ++row; +} + +Row * +bprevrow(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->lines, *last = b->lines + b->rows - 1; + + if (!row) + return nil; + + bboundary(b, &before_start, &before_end, &after_start, &after_end); + + if (row > first && row <= last) + return --row; + if (row == first) + return before_end; + if (row == before_start) + return nil; + if (row == after_start) + return last; + if (row == b->scroll.buf) + return &b->scroll.buf[b->scroll.size - 1]; + return --row; +} diff --git a/sys/cmd/dvtm/buffer.h b/sys/cmd/dvtm/buffer.h new file mode 100644 index 0000000..e69de29 diff --git a/sys/cmd/dvtm/config.h b/sys/cmd/dvtm/config.h new file mode 100644 index 0000000..ec70596 --- /dev/null +++ b/sys/cmd/dvtm/config.h @@ -0,0 +1,208 @@ +#define VERSION "1.0" + +/* valid curses attributes are listed below they can be ORed + * + * A_NORMAL Normal display (no highlight) + * A_STANDOUT Best highlighting mode of the terminal. + * A_UNDERLINE Underlining + * A_REVERSE Reverse video + * A_BLINK Blinking + * A_DIM Half bright + * A_BOLD Extra bright or bold + * A_PROTECT Protected mode + * A_INVIS Invisible or blank mode + */ + +enum { + DEFAULT, + BLUE, + GREEN, +}; + +static Color colors[] = { + [DEFAULT] = { .fg = -1, .bg = -1, .fg256 = -1, .bg256 = -1, }, + [BLUE] = { .fg = COLOR_BLUE, .bg = -1, .fg256 = 68, .bg256 = -1, }, + [GREEN] = { .fg = COLOR_GREEN, .bg = -1, .fg256 = 65, .bg256 = -1, }, +}; + +#define COLOR(c) COLOR_PAIR(colors[c].pair) +/* curses attributes for the currently focused window */ +#define SELECTED_ATTR (COLOR(GREEN) | A_NORMAL) +/* curses attributes for normal (not selected) windows */ +#define NORMAL_ATTR (COLOR(DEFAULT) | A_NORMAL) +/* curses attributes for a window with pending urgent flag */ +#define URGENT_ATTR NORMAL_ATTR +/* curses attributes for the status bar */ +#define BAR_ATTR (COLOR(GREEN) | A_NORMAL) +/* characters for beginning and end of status bar message */ +#define BAR_BEGIN '[' +#define BAR_END ']' +/* status bar (command line option -s) position */ +#define BAR_POS BAR_TOP /* BAR_BOTTOM, BAR_OFF */ +/* whether status bar should be hidden if only one client exists */ +#define BAR_AUTOHIDE false +/* master width factor [0.1 .. 0.9] */ +#define MFACT 0.5 +/* number of clients in master area */ +#define NMASTER 1 +/* scroll back buffer size in lines */ +#define SCROLL_HISTORY 500 +/* printf format string for the tag in the status bar */ +#define TAG_SYMBOL "[%s]" +/* curses attributes for the currently selected tags */ +#define TAG_SEL (COLOR(GREEN) | A_BOLD) +/* curses attributes for not selected tags which contain no windows */ +#define TAG_NORMAL (COLOR(DEFAULT) | A_NORMAL) +/* curses attributes for not selected tags which contain windows */ +#define TAG_OCCUPIED (COLOR(GREEN) | A_NORMAL) +/* curses attributes for not selected tags which with urgent windows */ +#define TAG_URGENT (COLOR(GREEN) | A_NORMAL | A_BLINK) + +static const char tags[][8] = { "1", "2", "3", "4", "5" }; + +/* by default the first layout entry is used */ +static Layout layouts[] = { + { "[]=", tile }, + { "+++", grid }, + { "TTT", bstack }, + { "[ ]", fullscreen }, +}; + +#define MOD ('`') +#define TAGKEYS(KEY,TAG) \ + { { MOD, 'v', KEY, }, { view, { tags[TAG] } } }, \ + { { MOD, 't', KEY, }, { tag, { tags[TAG] } } }, \ + { { MOD, 'V', KEY, }, { toggleview, { tags[TAG] } } }, \ + { { MOD, 'T', KEY, }, { toggletag, { tags[TAG] } } }, + +/* you can specifiy at most 3 arguments */ +static KeyBinding bindings[] = { + { { MOD, 'c', }, { create, { NULL } } }, + { { MOD, 'C', }, { create, { NULL, NULL, "$CWD" } } }, + { { MOD, 'x', 'x', }, { killclient, { NULL } } }, + { { MOD, 'j', }, { focusnext, { NULL } } }, + { { MOD, 'J', }, { focusdown, { NULL } } }, + { { MOD, 'K', }, { focusup, { NULL } } }, + { { MOD, 'H', }, { focusleft, { NULL } } }, + { { MOD, 'L', }, { focusright, { NULL } } }, + { { MOD, 'k', }, { focusprev, { NULL } } }, + { { MOD, 'f', }, { setlayout, { "[]=" } } }, + { { MOD, 'g', }, { setlayout, { "+++" } } }, + { { MOD, 'b', }, { setlayout, { "TTT" } } }, + { { MOD, 'm', }, { setlayout, { "[ ]" } } }, + { { MOD, ' ', }, { setlayout, { NULL } } }, + { { MOD, 'i', }, { incnmaster, { "+1" } } }, + { { MOD, 'd', }, { incnmaster, { "-1" } } }, + { { MOD, 'h', }, { setmfact, { "-0.05" } } }, + { { MOD, 'l', }, { setmfact, { "+0.05" } } }, + { { MOD, '.', }, { toggleminimize, { NULL } } }, + { { MOD, 's', }, { togglebar, { NULL } } }, + { { MOD, 'S', }, { togglebarpos, { NULL } } }, + { { MOD, 'M', }, { togglemouse, { NULL } } }, + { { MOD, '\n', }, { zoom , { NULL } } }, + { { MOD, '\r', }, { zoom , { NULL } } }, + { { MOD, '1', }, { focusn, { "1" } } }, + { { MOD, '2', }, { focusn, { "2" } } }, + { { MOD, '3', }, { focusn, { "3" } } }, + { { MOD, '4', }, { focusn, { "4" } } }, + { { MOD, '5', }, { focusn, { "5" } } }, + { { MOD, '6', }, { focusn, { "6" } } }, + { { MOD, '7', }, { focusn, { "7" } } }, + { { MOD, '8', }, { focusn, { "8" } } }, + { { MOD, '9', }, { focusn, { "9" } } }, + { { MOD, '\t', }, { focuslast, { NULL } } }, + { { MOD, 'q', 'q', }, { quit, { NULL } } }, + { { MOD, 'a', }, { togglerunall, { NULL } } }, + { { MOD, CTRL('L'), }, { redraw, { NULL } } }, + { { MOD, 'r', }, { redraw, { NULL } } }, + { { MOD, 'e', }, { copymode, { "dvtm-editor" } } }, + { { MOD, 'E', }, { copymode, { "dvtm-pager" } } }, + { { MOD, '/', }, { copymode, { "dvtm-pager", "/" } } }, + { { MOD, 'p', }, { paste, { NULL } } }, + { { MOD, KEY_PPAGE, }, { scrollback, { "-1" } } }, + { { MOD, KEY_NPAGE, }, { scrollback, { "1" } } }, + { { MOD, '?', }, { create, { "man dvtm", "dvtm help" } } }, + { { MOD, MOD, }, { send, { (const char []){MOD, 0} } } }, + { { KEY_SPREVIOUS, }, { scrollback, { "-1" } } }, + { { KEY_SNEXT, }, { scrollback, { "1" } } }, + { { MOD, '0', }, { view, { NULL } } }, + { { MOD, KEY_F(1), }, { view, { tags[0] } } }, + { { MOD, KEY_F(2), }, { view, { tags[1] } } }, + { { MOD, KEY_F(3), }, { view, { tags[2] } } }, + { { MOD, KEY_F(4), }, { view, { tags[3] } } }, + { { MOD, KEY_F(5), }, { view, { tags[4] } } }, + { { MOD, 'v', '0' }, { view, { NULL } } }, + { { MOD, 'v', '\t', }, { viewprevtag, { NULL } } }, + { { MOD, 't', '0' }, { tag, { NULL } } }, + TAGKEYS( '1', 0) + TAGKEYS( '2', 1) + TAGKEYS( '3', 2) + TAGKEYS( '4', 3) + TAGKEYS( '5', 4) +}; + +static const ColorRule colorrules[] = { + { "", A_NORMAL, &colors[DEFAULT] }, /* default */ +}; + +/* possible values for the mouse buttons are listed below: + * + * BUTTON1_PRESSED mouse button 1 down + * BUTTON1_RELEASED mouse button 1 up + * BUTTON1_CLICKED mouse button 1 clicked + * BUTTON1_DOUBLE_CLICKED mouse button 1 double clicked + * BUTTON1_TRIPLE_CLICKED mouse button 1 triple clicked + * BUTTON2_PRESSED mouse button 2 down + * BUTTON2_RELEASED mouse button 2 up + * BUTTON2_CLICKED mouse button 2 clicked + * BUTTON2_DOUBLE_CLICKED mouse button 2 double clicked + * BUTTON2_TRIPLE_CLICKED mouse button 2 triple clicked + * BUTTON3_PRESSED mouse button 3 down + * BUTTON3_RELEASED mouse button 3 up + * BUTTON3_CLICKED mouse button 3 clicked + * BUTTON3_DOUBLE_CLICKED mouse button 3 double clicked + * BUTTON3_TRIPLE_CLICKED mouse button 3 triple clicked + * BUTTON4_PRESSED mouse button 4 down + * BUTTON4_RELEASED mouse button 4 up + * BUTTON4_CLICKED mouse button 4 clicked + * BUTTON4_DOUBLE_CLICKED mouse button 4 double clicked + * BUTTON4_TRIPLE_CLICKED mouse button 4 triple clicked + * BUTTON_SHIFT shift was down during button state change + * BUTTON_CTRL control was down during button state change + * BUTTON_ALT alt was down during button state change + * ALL_MOUSE_EVENTS report all button state changes + * REPORT_MOUSE_POSITION report mouse movement + */ + +#ifdef NCURSES_MOUSE_VERSION +# define CONFIG_MOUSE /* compile in mouse support if we build against ncurses */ +#endif + +#define ENABLE_MOUSE true /* whether to enable mouse events by default */ + +#ifdef CONFIG_MOUSE +static Button buttons[] = { + { BUTTON1_CLICKED, { mouse_focus, { NULL } } }, + { BUTTON1_DOUBLE_CLICKED, { mouse_fullscreen, { "[ ]" } } }, + { BUTTON2_CLICKED, { mouse_zoom, { NULL } } }, + { BUTTON3_CLICKED, { mouse_minimize, { NULL } } }, +}; +#endif /* CONFIG_MOUSE */ + +static Cmd commands[] = { + /* create [cmd]: create a new window, run `cmd` in the shell if specified */ + { "create", { create, { NULL } } }, + /* focus : focus the window whose `DVTM_WINDOW_ID` is `win_id` */ + { "focus", { focusid, { NULL } } }, + /* tag [tag ...]: add +tag, remove -tag or set tag of the window with the given identifier */ + { "tag", { tagid, { NULL } } }, +}; + +/* gets executed when dvtm is started */ +static Action actions[] = { + { create, { NULL } }, +}; + +static char const * const keytable[] = { + /* add your custom key escape sequences */ +}; diff --git a/sys/cmd/dvtm/driver.c b/sys/cmd/dvtm/driver.c new file mode 100644 index 0000000..3e6a518 --- /dev/null +++ b/sys/cmd/dvtm/driver.c @@ -0,0 +1,335 @@ +#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); +} diff --git a/sys/cmd/dvtm/dvtm.c b/sys/cmd/dvtm/dvtm.c index 802f891..9d35b25 100644 --- a/sys/cmd/dvtm/dvtm.c +++ b/sys/cmd/dvtm/dvtm.c @@ -181,12 +181,14 @@ drawbar(void) wnoutrefresh(stdscr); } -static int +static +int show_border(void) { return (bar.pos != BAR_OFF) || (clients && clients->next); } -static void +static +void draw_border(Client *c) { char t = '\0'; int x, y, maxlen, attrs = NORMAL_ATTR; @@ -218,13 +220,17 @@ draw_border(Client *c) { wmove(c->window, y, x); } -static void -draw_content(Client *c) { +static +void +draw_content(Client *c) +{ vt_draw(c->term, c->window, c->has_title_line, 0); } -static void -draw(Client *c) { +static +void +draw(Client *c) +{ if (is_content_visible(c)) { redrawwin(c->window); draw_content(c); @@ -234,8 +240,10 @@ draw(Client *c) { wnoutrefresh(c->window); } -static void -draw_all(void) { +static +void +draw_all(void) +{ if (!nextvisible(clients)) { sel = nil; curs_set(0); @@ -259,8 +267,10 @@ draw_all(void) { draw(sel); } -static void -arrange(void) { +static +void +arrange(void) +{ uint m = 0, n = 0; for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { c->order = ++n; @@ -295,10 +305,13 @@ arrange(void) { draw_all(); } -static void -attach(Client *c) { +static +void +attach(Client *c) +{ if (clients) clients->prev = c; + c->next = clients; c->prev = nil; clients = c; @@ -306,8 +319,10 @@ attach(Client *c) { c->order = o; } -static void -attachafter(Client *c, Client *a) { /* attach c after a */ +static +void +attachafter(Client *c, Client *a) +{ /* attach c after a */ if (c == a) return; if (!a) @@ -324,14 +339,18 @@ attachafter(Client *c, Client *a) { /* attach c after a */ } } -static void -attachstack(Client *c) { +static +void +attachstack(Client *c) +{ c->snext = stack; stack = c; } -static void -detach(Client *c) { +static +void +detach(Client *c) +{ Client *d; if (c->prev) c->prev->next = c->next; @@ -345,8 +364,10 @@ detach(Client *c) { c->next = c->prev = nil; } -static void -settitle(Client *c) { +static +void +settitle(Client *c) +{ char *term, *t = title; if (!t && sel == c && *c->title) t = c->title; @@ -356,15 +377,19 @@ settitle(Client *c) { } } -static void -detachstack(Client *c) { +static +void +detachstack(Client *c) +{ Client **tc; - for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext); + for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext) + ; *tc = c->snext; } void -focus(Client *c) { +focus(Client *c) +{ if (!c) for (c = stack; c && !isvisible(c); c = c->snext); if (sel == c) @@ -394,10 +419,12 @@ focus(Client *c) { curs_set(c && !c->minimized && vt_cursor_visible(c->term)); } -static void -applycolorrules(Client *c) { +static +void +applycolorrules(Client *c) +{ const ColorRule *r = colorrules; - short fg = r->color->fg, bg = r->color->bg; + int fg = r->color->fg, bg = r->color->bg; attr_t attrs = r->attrs; for (uint i = 1; i < arrlen(colorrules); i++) { @@ -413,8 +440,10 @@ applycolorrules(Client *c) { vt_default_colors_set(c->term, attrs, fg, bg); } -static void -term_title_handler(Vt *term, const char *title) { +static +void +term_title_handler(Vt *term, const char *title) +{ Client *c = (Client *)vt_data_get(term); if (title) strncpy(c->title, title, sizeof(c->title) - 1); @@ -425,7 +454,8 @@ term_title_handler(Vt *term, const char *title) { applycolorrules(c); } -static void +static +void term_urgent_handler(Vt *term) { Client *c = (Client *)vt_data_get(term); c->urgent = true; @@ -684,9 +714,11 @@ viewprevtag(const char *args[]) tagschanged(); } -static void -keypress(int code) { - int key = -1; +static +void +keypress(int code) +{ + int key = -1; uint len = 1; char buf[8] = { '\e' }; @@ -710,6 +742,7 @@ keypress(int code) { vt_write(c->term, buf, len); else vt_keypress(c->term, code); + if (key != -1) vt_keypress(c->term, key); } @@ -718,8 +751,10 @@ keypress(int code) { } } -static void -mouse_setup(void) { +static +void +mouse_setup(void) +{ #ifdef CONFIG_MOUSE mmask_t mask = 0; @@ -755,8 +790,10 @@ getshell(void) { return "/bin/sh"; } -static void -setup(void) { +static +void +setup(void) +{ shell = getshell(); setlocale(LC_CTYPE, ""); initscr(); @@ -778,6 +815,7 @@ setup(void) { colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg); } resize_screen(); + struct sigaction sa; memset(&sa, 0, sizeof sa); sa.sa_flags = 0; @@ -792,7 +830,8 @@ setup(void) { sigaction(SIGPIPE, &sa, nil); } -static void +static +void destroy(Client *c) { if (sel == c) focusnextnm(nil); @@ -823,7 +862,8 @@ destroy(Client *c) { arrange(); } -static void +static +void cleanup(void) { while (clients) destroy(clients); @@ -840,7 +880,8 @@ cleanup(void) { unlink(cmdfifo.file); } -static char *getcwd_by_pid(Client *c) { +static +char *getcwd_by_pid(Client *c) { if (!c) return nil; char buf[32]; @@ -1664,11 +1705,12 @@ parse_args(int argc, char *argv[]) { } int -main(int argc, char *argv[]) { +main(int argc, char *argv[]) +{ KeyCombo keys; uint key_index = 0; memset(keys, 0, sizeof(keys)); - sigset_t emptyset, blockset; + sigset_t emptysigs, blockset; setenv("DVTM", VERSION, 1); if (!parse_args(argc, argv)) { @@ -1676,7 +1718,7 @@ main(int argc, char *argv[]) { startup(nil); } - sigemptyset(&emptyset); + sigemptyset(&emptysigs); sigemptyset(&blockset); sigaddset(&blockset, SIGWINCH); sigaddset(&blockset, SIGCHLD); @@ -1684,27 +1726,27 @@ main(int argc, char *argv[]) { while (running) { int r, nfds = 0; - fd_set rd; + fd_set rdrs; /* set of file descriptors we watch */ if (screen.need_resize) { resize_screen(); screen.need_resize = false; } - FD_ZERO(&rd); - FD_SET(STDIN_FILENO, &rd); + FD_ZERO(&rdrs); + FD_SET(STDIN_FILENO, &rdrs); if (cmdfifo.fd != -1) { - FD_SET(cmdfifo.fd, &rd); + FD_SET(cmdfifo.fd, &rdrs); nfds = cmdfifo.fd; } if (bar.fd != -1) { - FD_SET(bar.fd, &rd); + FD_SET(bar.fd, &rdrs); nfds = MAX(nfds, bar.fd); } - for (Client *c = clients; c; ) { + for (Client *c = clients; c;) { if (c->editor && c->editor_died) handle_editor(c); if (!c->editor && c->died) { @@ -1714,13 +1756,13 @@ main(int argc, char *argv[]) { continue; } int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app); - FD_SET(pty, &rd); + FD_SET(pty, &rdrs); nfds = MAX(nfds, pty); - c = c->next; + c = c->next; } doupdate(); - r = pselect(nfds + 1, &rd, nil, nil, nil, &emptyset); + r = pselect(nfds + 1, &rdrs, nil, nil, nil, &emptysigs); if (r < 0) { if (errno == EINTR) @@ -1729,11 +1771,13 @@ main(int argc, char *argv[]) { exit(EXIT_FAILURE); } - if (FD_ISSET(STDIN_FILENO, &rd)) { + if (FD_ISSET(STDIN_FILENO, &rdrs)) { + /* NOTE: this is the input handling step */ int code = getch(); if (code >= 0) { - keys[key_index++] = code; + keys[key_index++] = code; KeyBinding *binding = nil; + if (code == KEY_MOUSE) { key_index = 0; handle_mouse(); @@ -1752,18 +1796,19 @@ main(int argc, char *argv[]) { keypress(code); } } - if (r == 1) /* no data available on pty's */ + /* no data available on pty's */ + if (r == 1) continue; } - if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd)) + if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rdrs)) handle_cmdfifo(); - if (bar.fd != -1 && FD_ISSET(bar.fd, &rd)) + if (bar.fd != -1 && FD_ISSET(bar.fd, &rdrs)) handle_statusbar(); for (Client *c = clients; c; c = c->next) { - if (FD_ISSET(vt_pty_get(c->term), &rd)) { + if (FD_ISSET(vt_pty_get(c->term), &rdrs)) { if (vt_process(c->term) < 0 && errno == EIO) { if (c->editor) c->editor_died = true; diff --git a/sys/cmd/dvtm/dvtm.h b/sys/cmd/dvtm/dvtm.h index c36cb87..f89f517 100644 --- a/sys/cmd/dvtm/dvtm.h +++ b/sys/cmd/dvtm/dvtm.h @@ -13,37 +13,49 @@ #include #include +// #include #include #include "vt.h" -#ifdef PDCURSES -int ESCDELAY; -#endif - -#ifndef NCURSES_REENTRANT -#define set_escdelay(d) (ESCDELAY = (d)) -#endif +/* types */ +typedef struct Term Term; +typedef struct Screen Screen; +typedef struct Layout Layout; +typedef struct Window Window; +typedef struct Client Client; -typedef struct { - float mfact; - uint nmaster; - int history; - int w; - int h; +typedef struct Cmd Cmd; +typedef struct CmdFifo CmdFifo; +typedef struct Register Register; +typedef struct Editor Editor; + +typedef struct Button Button; +typedef struct KeyBinding KeyBinding; +typedef struct StatusBar StatusBar; + +struct Screen +{ + Term *backend; + float mfact; + uint nmaster; + int history; + int w, h; volatile sig_atomic_t need_resize; -} Screen; +}; -typedef struct { +struct Layout +{ const char *symbol; void (*arrange)(void); -} Layout; - -typedef struct Client Client; -struct Client { - WINDOW *window; - Vt *term; - Vt *editor, *app; +} ; + +struct Client +{ + Window *window; + Vt *term; + Vt *editor, *app; + /* meta data */ int editor_fds[2]; volatile sig_atomic_t editor_died; const char *cmd; @@ -51,26 +63,22 @@ struct Client { int order; pid_t pid; ushort id; - ushort x; - ushort y; - ushort w; - ushort h; + ushort x, y, w, h; bool has_title_line; bool minimized; bool urgent; volatile sig_atomic_t died; - Client *next; - Client *prev; - Client *snext; + Client *next, *prev, *snext; uint tags; }; +#if 0 typedef struct { - short fg; - short bg; - short fg256; - short bg256; - short pair; + int fg; + int bg; + int fg256; + int bg256; + int pair; } Color; typedef struct { @@ -78,6 +86,7 @@ typedef struct { attr_t attrs; Color *color; } ColorRule; +#endif #define ALT(k) ((k) + (161 - 'a')) @@ -92,7 +101,8 @@ typedef struct { #define MAX_ARGS 8 -typedef struct { +typedef struct +{ void (*cmd)(const char *args[]); const char *args[3]; } Action; @@ -101,24 +111,28 @@ typedef struct { typedef uint KeyCombo[MAX_KEYS]; -typedef struct { +struct KeyBinding +{ KeyCombo keys; Action action; -} KeyBinding; +}; -typedef struct { +struct Button +{ mmask_t mask; Action action; -} Button; +}; -typedef struct { +struct Cmd +{ const char *name; Action action; -} Cmd; +} ; enum { BAR_TOP, BAR_BOTTOM, BAR_OFF }; -typedef struct { +struct StatusBar +{ int fd; int pos, lastpos; bool autohide; @@ -126,26 +140,29 @@ typedef struct { ushort y; char text[512]; const char *file; -} StatusBar; +}; -typedef struct { +struct CmdFifo +{ int fd; const char *file; ushort id; -} CmdFifo; +}; -typedef struct { +struct Register +{ char *data; size_t len; size_t size; -} Register; +}; -typedef struct { +struct Editor +{ char *name; const char *argv[4]; bool filter; bool color; -} Editor; +}; #define TAGMASK ((1 << arrlen(tags)) - 1) @@ -206,7 +223,7 @@ void resize(Client *c, int x, int y, int w, int h); extern Screen screen; extern uint waw, wah, wax, way; extern Client *clients; -extern char *title; +extern char *title; void fibonacci(int s); void spiral(void); diff --git a/sys/cmd/dvtm/events.c b/sys/cmd/dvtm/events.c new file mode 100644 index 0000000..12b0518 --- /dev/null +++ b/sys/cmd/dvtm/events.c @@ -0,0 +1,1840 @@ +#include +#include +#include +#include + +#include + +#define iota(x) 1 << (x) +#define bufcount(in) in->buf.c - in->buf.b + +/* 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), +}; + +enum { + NodeKey, + NodeArr, +}; + +typedef struct Key Key; +typedef struct Node Node; +typedef struct Input 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; +}; + +struct Node +{ + int type; +}; + +struct KeyNode +{ + struct Node; + struct KeyInfo key; +}; + +struct ArrNode +{ + struct Node; + uchar min, max; + Node *arr[]; +}; + +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; +}; + +// ----------------------------------------------------------------------- +// loads data into trie + +static enum KeyEvent peekmousekey(Input *in, Key *key, ulong *nb); + +#define FuncNameMax 10 +static struct { + char *name; + int type; + int sym; + int mods; +} funcs[] = +{ + /* THIS LIST MUST REMAIN SORTED ALPHABETICALLY! */ + { "backspace", KeySym, SymBackspace, 0 }, + { "begin", KeySym, SymBegin, 0 }, + { "beg", KeySym, SymBegin, 0 }, + { "btab", KeySym, SymTab, ModShift}, + { "cancel", KeySym, SymCancel, 0 }, + { "clear", KeySym, SymClear, 0 }, + { "close", KeySym, SymClose, 0 }, + { "command", KeySym, SymCommand, 0 }, + { "copy", KeySym, SymCopy, 0 }, + { "dc", KeySym, SymDelete, 0 }, + { "down", KeySym, SymDown, 0 }, + { "end", KeySym, SymEnd, 0 }, + { "enter", KeySym, SymEnter, 0 }, + { "exit", KeySym, SymExit, 0 }, + { "find", KeySym, SymFind, 0 }, + { "help", KeySym, SymHelp, 0 }, + { "home", KeySym, SymHome, 0 }, + { "ic", KeySym, SymInsert, 0 }, + { "left", KeySym, SymLeft, 0 }, + { "mark", KeySym, SymMark, 0 }, + { "message", KeySym, SymMessage, 0 }, + { "move", KeySym, SymMove, 0 }, + { "next", KeySym, SymPagedown, 0 }, // Not quite, but it's the best we can do + { "npage", KeySym, SymPagedown, 0 }, + { "open", KeySym, SymOpen, 0 }, + { "options", KeySym, SymOptions, 0 }, + { "ppage", KeySym, SymPageup, 0 }, + { "previous", KeySym, SymPageup, 0 }, // Not quite, but it's the best we can do + { "print", KeySym, SymPrint, 0 }, + { "redo", KeySym, SymRedo, 0 }, + { "reference", KeySym, SymReference, 0 }, + { "refresh", KeySym, SymRefresh, 0 }, + { "replace", KeySym, SymReplace, 0 }, + { "restart", KeySym, SymRestart, 0 }, + { "resume", KeySym, SymResume, 0 }, + { "right", KeySym, SymRight, 0 }, + { "save", KeySym, SymSave, 0 }, + { "select", KeySym, SymSelect, 0 }, + { "suspend", KeySym, SymSuspend, 0 }, + { "undo", KeySym, SymUndo, 0 }, + { "up", KeySym, SymUp, 0 }, + { nil }, +}; + +// ----------------------------------------------------------------------- +// utility functions + +static +int +stricmp(char *s, char *t) +{ + if (s == nil) + if (t == nil) + return 0; + else + return -strlen(t); + else + if (t == nil) + return +strlen(s); + + int d; + for (; *s && *t; s++, t++) { + d = tolower(*s) - tolower(*t); + if (d < 0 || d > 0) + return d; + } + + /* XXX: not sure about passing in 0 here */ + return tolower(*s) - tolower(*t); +} + +// ----------------------------------------------------------------------- +// bytes -> keysymbols + +static +Node * +keynode(int type, int sym, int mask, int set) +{ + struct KeyNode *n; + n = malloc(sizeof(*n)); + if (!n) + panicf("out of memory"); + + n->type = NodeKey; + n->key.type = type; + n->key.sym = sym; + n->key.modmask = mask; + n->key.modset = set; + + return (Node *)n; +} + +static +Node * +arrnode(uchar min, uchar max) +{ + int nb, i; + struct ArrNode *n; + nb = ((int)max-min+1)*sizeof(*n->arr); + n = malloc(sizeof(*n) + nb); + if (!n) + panicf("out of memory"); + + n->type = NodeArr; + n->min = min, n->max = max; + for (i = 0; i <= max-min; i++) + n->arr[i] = nil; + + return (Node *)n; +} + + +static +Node * +nlookup(Node *n, uchar b) +{ + struct ArrNode *arr; + switch (n->type) { + case NodeKey: + panicf("attempting to subdivide a leaf key node"); + return nil; + case NodeArr: + arr = (struct ArrNode *)n; + if (b < arr->min || b > arr->max) + return nil; + return arr->arr[b - arr->min]; + default: + panicf("unrecognized key node type"); + return nil; + } +} + +static +Node * +compress(Node *root) +{ + int i; + uchar min, max; + struct ArrNode *n, *new; + + if (!root) + return root; + + switch (root->type) { + case NodeKey: + return root; + case NodeArr: + n = (struct ArrNode *)root; + /* find the zeros */ + for (min = 0; !n->arr[min]; min++) { + if (min == 255 && !n->arr[min]) { + free(n); + return arrnode(1, 0); + } + } + + for (max = 0xff; !n->arr[max]; max--) + ; + + new = (struct ArrNode *)arrnode(min, max); + + for (i = min; i <= max; i++) + new->arr[i-min] = compress(n->arr[i]); + + free(n); + return (Node*)new; + default: + panicf("unrecognized key node type"); + return nil; + } + + return root; +} + +static +void +teardown(Node *n) +{ + int i; + struct ArrNode *arr; + + switch (n->type) { + case NodeKey: + break; + case NodeArr: + arr = (struct ArrNode *)n; + for (i = arr->min; i <= arr->max; i++) + if (arr->arr[i - arr->min]) + teardown(arr->arr[i - arr->min]); + break; + default: + return; + } + + free(n); +} + +static +int +insert(Node *root, Node *n, char *seq) +{ + int pos; + uchar b; + Node *nn; + struct ArrNode *a; + + for (pos = 0; (b = seq[pos]); pos++) { + nn = nlookup(root, b); + if (!nn) + break; + root = nn; + a = (struct ArrNode *)root; + } + + for (; (b = seq[pos]); pos++) { + nn = (seq[pos+1]) ? arrnode(0, 0xff) : n; + if (!nn) + return 0; + + if (root->type != NodeArr) + panicf("inserted child node onto leaf 'key' node"); + + a = (struct ArrNode*)root; + if (b < a->min || b > a->max) + panicf("out of bound trie insertion"); + + a->arr[b-a->min] = root = nn; + } + + return 1; +} + +/* unibilium helpers */ +static +enum unibi_string +nametostr(char *name) +{ + enum unibi_string ret; + for (ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++) + if (!strcmp(unibi_name_str(ret), name)) + return ret; + + return -1; +} + +static +char * +unibi_lookup(unibi_term *info, char *name) +{ + enum unibi_string idx; + if ((idx = nametostr(name)) == -1) + return nil; + + return (char*)unibi_get_str(info, idx); +} + +static +Node * +tryloadkey(unibi_term *info, char *name, char **val, struct KeyInfo *key) +{ + char *v; + + v = unibi_lookup(info, name); + if (!v || v[0] == 0) + return nil; + + *val = v; + return keynode(key->type, key->sym, key->modmask, key->modset); +} + +static +Node * +loadtermkeys(unibi_term *info) +{ + int i; + struct Node *root, *n; + char *val, name[5 + FuncNameMax + 1]; + + root = arrnode(0, 0xff); + if (!root) + panicf("out of memory"); + + /* load key syms */ + for (i = 0; funcs[i].name; i++) { + sprintf(name, "key_%s", funcs[i].name); + n = tryloadkey(info, name, &val, &(struct KeyInfo){ + .type = funcs[i].type, + .sym = funcs[i].sym, + .modmask = funcs[i].mods, + .modset = funcs[i].mods, + }); + + /* try shift modified */ + if (!n) + n = tryloadkey(info, name, &val, &(struct KeyInfo){ + .type = funcs[i].type, + .sym = funcs[i].sym, + .modmask = funcs[i].mods | ModShift, + .modset = funcs[i].mods | ModShift, + }); + + /* give up */ + if (n) + insert(root, n, val); + } + + /* load function keys */ + for (i = 1; i < 0xff; i++) { + sprintf(name, "key_f%d", i); + n = tryloadkey(info, name, &val, &(struct KeyInfo){ + .type = KeyFunc, + .sym = i, + .modmask = 0, + .modset = 0, + }); + if (!n) + break; + + insert(root, n, val); + } + + /* load mouse keys */ + val = unibi_lookup(info, "key_mouse"); + if (val && !strcmp(val, "\x1b[M")) { + n = keynode(KeyMouse, 0, 0, 0); + insert(root, n, val); + } + + return compress(root); +} + +static +enum KeyEvent +peeksym(Input *in, Key *key, int force, ulong *nb) +{ + int res; + uchar *b, *o; + Node *root; + struct KeyNode *kn; + + root = in->keys; + b = in->buf.b; + while (b <= in->buf.c) { + printf("checking '%s'\n", b); + root = nlookup(root, *b); + if (!root) + break; + b++; + + if (root->type != NodeKey) + continue; + + kn = (struct KeyNode*)root; + if (kn->key.type == KeyMouse) { + o = in->buf.b, in->buf.b = b; + res = peekmousekey(in, key, nb); + in->buf.b = o; + + if (res == EvKey) + *nb += b - in->buf.b; + + return res; + } + key->type = kn->key.type; + key->mods = kn->key.modset; + key->code.sym = kn->key.type; + *nb = b - in->buf.b; + + return EvKey; + } + + if (root && !force) + return EvAgain; + + return EvNil; +} + +// ----------------------------------------------------------------------- +// misc commands + +static +inline +void +setpos(Key *key, int row, int col) +{ + row = MIN(row, 0xfff); + col = MIN(col, 0xfff); + + key->code.mouse[1] = (row & 0x0ff); + key->code.mouse[2] = (col & 0x0ff); + key->code.mouse[3] = (row & 0xf00) >> 8 | (col & 0x300) >> 4; +} + +static +inline +void +getpos(Key *key, int *row, int *col) +{ + if(col) + *col = (uchar)key->code.mouse[1] | ((uchar)key->code.mouse[3] & 0x0f) << 8; + + if(row) + *row = (uchar)key->code.mouse[2] | ((uchar)key->code.mouse[3] & 0x70) << 4; +} + +// ----------------------------------------------------------------------- +// csi/ss3 commands + +static int csiinit; + +static struct KeyInfo ss3[64]; +static char ss3kpalts[64]; + +static struct KeyInfo csiss3[64]; +static enum KeyEvent (*do_csi[64])(Input *in, Key *key, int cmd, int narg, long *arg); + +static struct KeyInfo csifuncs[35]; + +/* csi/ss3 cmd keys */ +static +enum KeyEvent +do_csi_full(Input *in, Key *key, int cmd, int narg, long *arg) +{ + if(narg > 1 && arg[1] != -1) + key->mods = arg[1] - 1; + else + key->mods = 0; + + key->type = csiss3[cmd - 0x40].type; + key->code.sym = csiss3[cmd - 0x40].sym; + key->mods &= ~(csiss3[cmd - 0x40].modmask); + key->mods |= csiss3[cmd - 0x40].modset; + + if(key->code.sym == SymUnknown) + return EvNil; + + return EvKey; +} + +static +void +put_csi_full(int type, int sym, int modset, int modmask, uchar cmd) +{ + if(cmd < 0x40 || cmd >= 0x80) + return; + + csiss3[cmd - 0x40].type = type; + csiss3[cmd - 0x40].sym = sym; + csiss3[cmd - 0x40].modset = modset; + csiss3[cmd - 0x40].modmask = modmask; + + do_csi[cmd - 0x40] = &do_csi_full; +} + +/* ss3 kpad keys */ +static +void +put_ss3_kpalt(int type, int sym, uchar cmd, char kpalt) +{ + if(cmd < 0x40 || cmd >= 0x80) + return; + + ss3[cmd - 0x40].type = type; + ss3[cmd - 0x40].sym = sym; + ss3[cmd - 0x40].modset = 0; + ss3[cmd - 0x40].modmask= 0; + ss3kpalts[cmd - 0x40] = kpalt; +} + +/* csi number ~func keys */ +static void emitcodepoint(Input *in, rune r, Key *key); + +static +enum KeyEvent +do_csi_func(Input *in, Key *key, int cmd, int narg, long *arg) +{ + if (narg > 1 && arg[1] != -1) + key->mods = arg[1] - 1; + else + key->mods = 0; + + key->type = KeySym; + + if (arg[0] == 27) { + int mod = key->mods; + emitcodepoint(in, (rune)arg[2], key); + key->mods |= mod; + } else if (arg[0] >= 0 && arg[0] < arrlen(csifuncs)) { + key->type = csifuncs[arg[0]].type; + key->code.sym = csifuncs[arg[0]].sym; + key->mods &= ~(csifuncs[arg[0]].modmask); + key->mods |= csifuncs[arg[0]].modset; + } else + key->code.sym = SymUnknown; + + if (key->code.sym == SymUnknown) + return EvNil; + + return EvKey; +} + +static +void +put_csi_func(int type, int sym, int num) +{ + if(num >= arrlen(csifuncs)) + return; + + csifuncs[num].type = type; + csifuncs[num].sym = sym; + csifuncs[num].modset = 0; + csifuncs[num].modmask = 0; + + do_csi['~' - 0x40] = &do_csi_func; +} + +/* CSI u extended unicode keys */ +static +enum KeyEvent +do_csi_u(Input *in, Key *key, int cmd, int narg, long *arg) +{ + switch(cmd) { + case 'u': { + if(narg > 1 && arg[1] != -1) + key->mods = arg[1] - 1; + else + key->mods = 0; + + int mod = key->mods; + key->type = KeySym; + emitcodepoint(in, arg[0], key); + key->mods |= mod; + + return EvKey; + } + default: + return EvNil; + } +} + +/* csi m/M mouse events */ + +static +enum KeyEvent +do_csi_m(Input *in, Key *key, int cmd, int narg, long *arg) +{ + int initial = cmd >> 8; + cmd &= 0xff; + + switch(cmd) { + case 'M': + case 'm': + break; + default: + return EvNil; + } + + // rxvt protocol + if(!initial && narg >= 3) { + key->type = KeyMouse; + key->code.mouse[0] = arg[0]; + + key->mods = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + setpos(key, arg[1], arg[2]); + + return EvKey; + } + + if(initial == '<' && narg >= 3) { // SGR protocol + key->type = KeyMouse; + key->code.mouse[0] = arg[0]; + + key->mods = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + setpos(key, arg[1], arg[2]); + + if(cmd == 'm') // release + key->code.mouse[3] |= 0x80; + + return EvKey; + } + + return EvNil; +} + +/* csi ? R position events */ + +static +enum KeyEvent +do_csi_R(Input *in, Key *key, int cmd, int narg, long *arg) +{ + switch(cmd) { + case 'R'|'?'<<8: + if(narg < 2) + return EvNil; + key->type = KeyPosition; + setpos(key, arg[1], arg[0]); + return EvKey; + default: + return do_csi_full(in, key, cmd, narg, arg); + } +} + +/* csi $y mode status events */ + +static +enum KeyEvent +do_csi_y(Input *in, Key *key, int cmd, int narg, long *arg) +{ + switch (cmd) { + case 'y'|'$'<<16: + case 'y'|'$'<<16 | '?'<<8: + if (narg < 2) + return EvNil; + + key->type = KeyModeReport; + key->code.mouse[0] = (cmd >> 8); + key->code.mouse[1] = arg[0] >> 8; + key->code.mouse[2] = arg[0] & 0xff; + key->code.mouse[3] = arg[1]; + return EvKey; + + default: + return EvNil; + } +} + +/* parse csi events */ +static +enum KeyEvent +parse_csi(Input *in, ulong introlen, ulong *csi_len, ulong *nargs, long args[], unsigned long *commandp) +{ + ulong csi_end = introlen; + + while (csi_end < bufcount(in)) { + if (in->buf.b[csi_end] >= 0x40 && in->buf.b[csi_end] < 0x80) + break; + csi_end++; + } + + if(csi_end >= bufcount(in)) + return EvAgain; + + uchar cmd = in->buf.b[csi_end]; + *commandp = cmd; + + char present = 0; + int argi = 0; + + ulong p = introlen; + + // See if there is an initial byte + if (in->buf.b[p] >= '<' && in->buf.b[p] <= '?') { + *commandp |= (in->buf.b[p] << 8); + p++; + } + + // Now attempt to parse out up number;number;... separated values + while (p < csi_end) { + uchar c = in->buf.b[p]; + + if (c >= '0' && c <= '9') { + if (!present) { + args[argi] = c - '0'; + present = 1; + } else { + args[argi] = (args[argi] * 10) + c - '0'; + } + } else if(c == ';') { + if (!present) + args[argi] = -1; + present = 0; + argi++; + + if(argi > 16) + break; + } else if (c >= 0x20 && c <= 0x2f) { + *commandp |= c << 16; + break; + } + p++; + } + + if(present) + argi++; + + *nargs = argi; + *csi_len = csi_end + 1; + + return EvKey; +} + +static +void +loadctrlkeys(void) +{ + int i; + for(i = 0; i < 64; i++) { + csiss3[i].sym = SymUnknown; + ss3[i].sym = SymUnknown; + ss3kpalts[i] = 0; + } + + for(i = 0; i < arrlen(csifuncs); i++) + csifuncs[i].sym = SymUnknown; + + put_csi_full(KeySym, SymUp, 0, 0, 'A'); + put_csi_full(KeySym, SymDown, 0, 0, 'B'); + put_csi_full(KeySym, SymRight,0, 0, 'C'); + put_csi_full(KeySym, SymLeft, 0, 0, 'D'); + put_csi_full(KeySym, SymBegin,0, 0, 'E'); + put_csi_full(KeySym, SymEnd, 0, 0, 'F'); + put_csi_full(KeySym, SymHome, 0, 0, 'H'); + put_csi_full(KeySym, 1, 0, 0, 'P'); + put_csi_full(KeySym, 2, 0, 0, 'Q'); + put_csi_full(KeySym, 3, 0, 0, 'R'); + put_csi_full(KeySym, 4, 0, 0, 'S'); + + put_csi_full(KeySym, SymTab, ModShift, ModShift, 'Z'); + + put_ss3_kpalt(KeySym, SymKpenter, 'M', 0); + put_ss3_kpalt(KeySym, SymKpequals, 'X', '='); + put_ss3_kpalt(KeySym, SymKpmult, 'j', '*'); + put_ss3_kpalt(KeySym, SymKpplus, 'k', '+'); + put_ss3_kpalt(KeySym, SymKpcomma, 'l', ','); + put_ss3_kpalt(KeySym, SymKpminus, 'm', '-'); + put_ss3_kpalt(KeySym, SymKpperiod, 'n', '.'); + put_ss3_kpalt(KeySym, SymKpdiv, 'o', '/'); + put_ss3_kpalt(KeySym, SymKp0, 'p', '0'); + put_ss3_kpalt(KeySym, SymKp1, 'q', '1'); + put_ss3_kpalt(KeySym, SymKp2, 'r', '2'); + put_ss3_kpalt(KeySym, SymKp3, 's', '3'); + put_ss3_kpalt(KeySym, SymKp4, 't', '4'); + put_ss3_kpalt(KeySym, SymKp5, 'u', '5'); + put_ss3_kpalt(KeySym, SymKp6, 'v', '6'); + put_ss3_kpalt(KeySym, SymKp7, 'w', '7'); + put_ss3_kpalt(KeySym, SymKp8, 'x', '8'); + put_ss3_kpalt(KeySym, SymKp9, 'y', '9'); + + put_csi_func(KeySym, SymFind, 1); + put_csi_func(KeySym, SymInsert, 2); + put_csi_func(KeySym, SymDelete, 3); + put_csi_func(KeySym, SymSelect, 4); + put_csi_func(KeySym, SymPageup, 5); + put_csi_func(KeySym, SymPagedown, 6); + put_csi_func(KeySym, SymHome, 7); + put_csi_func(KeySym, SymEnd, 8); + + put_csi_func(KeyFunc, 1, 11); + put_csi_func(KeyFunc, 2, 12); + put_csi_func(KeyFunc, 3, 13); + put_csi_func(KeyFunc, 4, 14); + put_csi_func(KeyFunc, 5, 15); + put_csi_func(KeyFunc, 6, 17); + put_csi_func(KeyFunc, 7, 18); + put_csi_func(KeyFunc, 8, 19); + put_csi_func(KeyFunc, 9, 20); + put_csi_func(KeyFunc, 10, 21); + put_csi_func(KeyFunc, 11, 23); + put_csi_func(KeyFunc, 12, 24); + put_csi_func(KeyFunc, 13, 25); + put_csi_func(KeyFunc, 14, 26); + put_csi_func(KeyFunc, 15, 28); + put_csi_func(KeyFunc, 16, 29); + put_csi_func(KeyFunc, 17, 31); + put_csi_func(KeyFunc, 18, 32); + put_csi_func(KeyFunc, 19, 33); + put_csi_func(KeyFunc, 20, 34); + + do_csi['u' - 0x40] = &do_csi_u; + do_csi['M' - 0x40] = &do_csi_m; + do_csi['m' - 0x40] = &do_csi_m; + do_csi['R' - 0x40] = &do_csi_R; + do_csi['y' - 0x40] = &do_csi_y; + + csiinit = 1; +} + +static +enum KeyEvent +peekcsi(Input *in, ulong introlen, Key *key, int force, ulong *nb) +{ + ulong csi_len; + long arg[16]; + ulong nargs = arrlen(arg); + ulong cmd; + + enum KeyEvent ev = parse_csi(in, introlen, &csi_len, &nargs, arg, &cmd); + + if (ev== EvAgain) { + if(!force) + return ev; + + emitcodepoint(in, '[', key); + key->mods |= ModAlt; + *nb = introlen; + return EvKey; + } + // Mouse in X10 encoding consumes the next 3 bytes also + if (cmd == 'M' && nargs < 3) { + in->buf.b += csi_len; + ev = peekmousekey(in, key, nb); + in->buf.b -= csi_len; + + if (ev == EvKey) + *nb += csi_len; + + return ev; + } + + ev = EvNil; + + // We know from the logic above that cmd must be >= 0x40 and < 0x80 + if (do_csi[(cmd & 0xff) - 0x40]) + ev = (*do_csi[(cmd & 0xff) - 0x40])(in, key, cmd, nargs, arg); + + if (ev == EvNil) { + key->type = KeyUnknownCSI; + key->code.num = cmd; + key->mods = 0; + + in->buf.off = csi_len - introlen; + *nb = introlen; /* dont advance yet */ + return EvKey; + } + + *nb = csi_len; + return ev; +} + +static +enum KeyEvent +peekss3(Input *in, ulong introlen, Key *key, int force, ulong *nb) +{ + if(bufcount(in) < introlen + 1) { + if(!force) + return EvAgain; + + emitcodepoint(in, 'O', key); + key->mods |= ModAlt; + *nb= bufcount(in); + return EvKey; + } + uchar cmd = in->buf.b[introlen]; + + if(cmd < 0x40 || cmd >= 0x80) + return EvNil; + + key->type = csiss3[cmd - 0x40].type; + key->code.sym = csiss3[cmd - 0x40].sym; + key->mods = csiss3[cmd - 0x40].modset; + + if (key->code.sym == SymUnknown) { + if (in->flags & FlagConvertKP && ss3kpalts[cmd - 0x40]) { + key->type = KeyUnicode; + key->code.pt = ss3kpalts[cmd - 0x40]; + key->mods = 0; + + key->utf8[0] = key->code.pt; + key->utf8[1] = 0; + } else { + key->type = ss3[cmd - 0x40].type; + key->code.sym = ss3[cmd - 0x40].sym; + key->mods = ss3[cmd - 0x40].modset; + } + } + + if(key->code.sym == SymUnknown) + return EvNil; + + *nb = introlen + 1; + + return EvKey; +} + +static +enum KeyEvent +peekctrl(Input *in, ulong introlen, Key *key, int force, ulong *nb) +{ + ulong str_end = introlen; + + while(str_end < bufcount(in)) { + if (in->buf.b[str_end] == 0x9c) // ST + break; + if (in->buf.b[str_end] == 0x1b && + (str_end + 1) < bufcount(in) && + in->buf.b[str_end+1] == 0x5c) // ESC-prefixed ST + break; + + str_end++; + } + + if (str_end >= bufcount(in)) + return EvAgain; + + *nb = str_end + 1; + if(in->buf.b[str_end] == 0x1b) + (*nb)++; + + // XXX: read carefully + if(in->savedcsi) + free(in->savedcsi); + + ulong len = str_end - introlen; + + in->nsavedcsi++; + in->savedcsi = malloc(len + 1); + + strncpy(in->savedcsi, (char *)in->buf.b + introlen, len); + in->savedcsi[len] = 0; + + key->type = (in->buf.b[introlen-1] & 0x1f) == 0x10 ? KeyDCS : KeyOSC; + key->code.num = in->nsavedcsi; + key->mods = 0; + + return EvKey; +} + +static +enum KeyEvent +peekesc(Input *in, Key *key, int force, ulong *nb) +{ + if (bufcount(in) == 0) + return in->closed ? EvEOF : EvNil; + + switch (*in->buf.b) { + case 0x1b: + if(bufcount(in) < 2) + return EvNil; + + switch(in->buf.b[1]) { + case 0x4f: // ESC-prefixed SS3 + return peekss3(in, 2, key, force, nb); + + case 0x50: // ESC-prefixed DCS + case 0x5d: // ESC-prefixed OSC + return peekctrl(in, 2, key, force, nb); + + case 0x5b: // ESC-prefixed CSI + return peekcsi(in, 2, key, force, nb); + } + + return EvNil; + + case 0x8f: // SS3 + return peekss3(in, 1, key, force, nb); + + case 0x90: // DCS + case 0x9d: // OSC + return peekctrl(in, 1, key, force, nb); + + case 0x9b: // CSI + return peekcsi(in, 1, key, force, nb); + } + + return EvNil; +} + + +// ----------------------------------------------------------------------- +// internal functions + +static +int +registername(Input *in, int sym, char *name) +{ + if (!sym) + sym = in->nkeynm; + + if(sym >= in->nkeynm) { + char **tmp = realloc(in->keynm, sizeof(*tmp) * (sym + 1)); + if(!tmp) + return -1; + + in->keynm = tmp; + + // Fill in the hole + for(int i = in->nkeynm; i < sym; i++) + in->keynm[i] = nil; + + in->nkeynm = sym + 1; + } + + in->keynm[sym] = name; + + return sym; +} + +static +int +registerc0(Input *in, int sym, int modset, int modmask, uchar ctrl, char *name) +{ + if(ctrl >= 0x20) { + errno = EINVAL; + return -1; + } + + if (name) + sym = registername(in, sym, name); + + in->c0[ctrl].sym = sym; + in->c0[ctrl].modset = modset; + in->c0[ctrl].modmask = modmask; + + return sym; +} + +static +void +advance(Input *in, ulong nb) +{ + if (in->buf.c < in->buf.b + nb) { + in->buf.b = in->buf.c; + return; + } + + in->buf.b += nb; +} + +static +void +slidebuffer(Input *in) +{ + static const ulong halfway = arrlen(in->buf.bytes) / 2; + if (in->buf.b - in->buf.bytes > halfway) { + memmove(in->buf.bytes, in->buf.bytes + halfway, halfway); + in->buf.b -= halfway; + in->buf.c -= halfway; + } +} + +static +void +harmonize(Input *in, Key *key) +{ + int flags = in->hflag; + + if (flags & HarmonizeSpace) { + if (key->type == KeyUnicode && key->code.pt == 0x20) { + key->type = KeySym; + key->code.sym = SymSpace; + } + } else { + if (key->type == KeySym && key->code.sym == SymSpace) { + key->type = KeyUnicode; + key->code.pt = 0x20; + utf8·runetobyte((char*)key->utf8, &key->code.pt); + } + } + + if (flags & HarmonizeDelBS) { + if (key->type == KeySym && key->code.sym == SymDel) { + key->code.sym = SymBackspace; + } + } +} + +static +void +emitcodepoint(Input *in, rune r, Key *key) +{ + if (r == 0) { + key->type = KeySym; + key->code.sym = SymSpace; + key->mods = ModCtrl; + goto harmonize; + } + if (r < 0x20) { + key->code.pt = 0; + key->mods = 0; + if (!(in->flags & FlagNoInterpret) && in->c0[r].sym != SymUnknown) { + key->code.sym = in->c0[r].sym; + key->mods |= in->c0[r].modset; + } + if (!key->code.sym) { + key->type = KeyUnicode; + if (r+0x40 >= 'A' && r+0x40 <= 'Z') + // it's a letter - use lowercase instead + key->code.pt = r + 0x60; + else + key->code.pt = r + 0x40; + key->mods = ModCtrl; + } else + key->type = KeySym; + goto harmonize; + } + if (r == 0x7f && !(in->flags & FlagNoInterpret)) { + // ascii del + key->type = KeySym; + key->code.sym = SymDel; + key->mods = 0; + goto harmonize; + } + if (r >= 0x20 && r < 0x80) { + // ascii lowbyte range + key->type = KeyUnicode; + key->code.pt = r; + key->mods = 0; + goto harmonize; + } + if (r >= 0x80 && r < 0xa0) { + // UTF-8 never starts with a C1 byte. So we can be sure of these + key->type = KeyUnicode; + key->code.pt = r - 0x40; + key->mods = ModCtrl|ModAlt; + goto harmonize; + } + key->type = KeyUnicode; + key->code.pt = r; + key->mods = 0; + +harmonize: + harmonize(in, key); + utf8·runetobyte((char*)key->utf8, &key->code.pt); +} + +static +enum KeyEvent +peekmousekey(Input *in, Key *key, ulong *nb) +{ + if (in->buf.c - in->buf.b < 3) + return EvAgain; + + key->type = KeyMouse; + key->code.mouse[0] = in->buf.c[0] - 0x20; + key->code.mouse[1] = in->buf.c[1] - 0x20; + key->code.mouse[2] = in->buf.c[2] - 0x20; + key->code.mouse[3] = 0; + + key->mods = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + *nb = 3; + return EvKey; +} + +enum KeyEvent peekkey(Input *in, Key *key, int force, ulong *nb); + +static +enum KeyEvent +peeksimplekey(Input *in, Key *key, int force, ulong *nb) +{ + uchar c, *b; + int n; + rune r; + enum KeyEvent ev; + + b = in->buf.b; + c = *b; + + if (c == 0x1b) { + if (bufcount(in) == 1) { + if (!force) + return EvAgain; + goto ascii; + } + in->buf.b++; + ev = peekkey(in, key, force, nb); + in->buf.b--; + + switch (ev) { + case EvKey: + key->mods |= ModAlt; + (*nb)++; + /* fallthrough */ + case EvNil: case EvEOF: + case EvAgain: case EvErr: + return ev; + } + } + if (c == 0xa0) + goto ascii; + if (in->flags & FlagUTF8) { + n = utf8·bytetorune(&r, (char*)in->buf.b); + *nb = n; /* store the number of bytes */ + if (n > bufcount(in)) { + if (!force) + return EvAgain; + r = RuneErr; + *nb = bufcount(in); + } + key->type = KeyUnicode; + key->mods = 0; + goto utf8; + } + /* if we are here just emit raw byte */ + key->type = KeyUnicode; + key->code.pt = c; + key->mods = 0; + key->utf8[0] = c; + key->utf8[1] = 0; + *nb = 1; + return EvKey; + +ascii: + *nb = 1; + r = c; +utf8: + emitcodepoint(in, r, key); + return EvKey; +} + +// ----------------------------------------------------------------------- +// exported functions + +Input * +makeinput(int fd, int flags, unibi_term *info) +{ + int i; + Input *in; + char *e; + + if (!(in = malloc(sizeof(in)))) + panicf("out of memory"); + + in->fd = fd; + if (!(flags & (FlagRaw|FlagUTF8))) { + if (((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) && + (e = strchr(e, '.')) && e++ && (!stricmp(e, "UTF-8") || !stricmp(e, "UTF8"))) + flags |= FlagUTF8; + else + flags |= FlagRaw; + } + in->flags = flags; + in->wait = 50; /* in msec */ + in->closed = 0; + in->started = 0; + in->hasold = 0; + in->keys = loadtermkeys(info); + + /* initialize buffer */ + in->buf.c = in->buf.b = in->buf.bytes; + in->buf.e = arrend(in->buf.bytes); + in->buf.off = 0; + memset(in->buf.bytes, 0, arrlen(in->buf.bytes)); + + /* initialize names */ + for (i = 0; i < 32; i++) + in->c0[i].sym = SymNone; + + registerc0(in, SymTab, 0x09, 0, 0, nil); + registerc0(in, SymEnter, 0x0d, 0, 0, nil); + registerc0(in, SymEscape, 0x1b, 0, 0, nil); + + /* load in csi */ + in->nsavedcsi = 0; + in->savedcsi = nil; + loadctrlkeys(); + + return in; +} + +void +freeinput(Input *in) +{ + // free(in); +} + +int +startrecord(Input *in) +{ + struct termios new; + if (in->started) + return 1; + + if (in->fd != -1 && !(in->flags & FlagNoTermIOS)) { + if (tcgetattr(in->fd, &new) == 0) { + in->oldterm = new; + in->hasold = 1; + + new.c_iflag &= ~(IXON|INLCR|ICRNL); + new.c_lflag &= ~(ICANON|ECHO|IEXTEN); + + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + + if (in->flags & FlagCtrlC) + new.c_lflag &= ~ISIG; + else { + /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */ + new.c_cc[VQUIT] = _POSIX_VDISABLE; + new.c_cc[VSUSP] = _POSIX_VDISABLE; + /* Some OSes have Ctrl-Y==VDSUSP */ +# ifdef VDSUSP + new.c_cc[VDSUSP] = _POSIX_VDISABLE; +# endif + } + tcsetattr(in->fd, TCSANOW, &new); + } + } + + in->started = 1; + return 1; +} + +int +stoprecord(Input *in) +{ + if (!in->started) + return 1; + + if (in->hasold) + tcsetattr(in->fd, TCSANOW, &in->oldterm); + + in->started = 0; + return 1; +} + +enum KeyEvent +peekkey(Input *in, Key *key, int force, ulong *nb) +{ + int i, again = 0; + enum KeyEvent ev; + static enum KeyEvent (*peek[2])(Input *, Key *, int, ulong *) = { peeksym, peekesc }; + + if (!in->started) { + errno = EINVAL; + return EvErr; + } + + if (in->buf.off) { + in->buf.b += in->buf.off; + in->buf.off = 0; + } + + for (i = 0; i < arrlen(peek); i++) { + ev = peek[i](in, key, force, nb); + switch (ev) { + case EvKey: + slidebuffer(in); + /* fallthrough */ + case EvEOF: + case EvErr: + return ev; + case EvAgain: + if (!force) + again = 1; + /* fallthrough */ + case EvNil: + break; + } + } + if (again) + return EvAgain; + + return peeksimplekey(in, key, force, nb); +} + +enum KeyEvent +getkey(Input *in, Key *key) +{ + ulong nb; + enum KeyEvent ev; + + ev = peekkey(in, key, 0, &nb); + switch (ev) { + case EvKey: + advance(in, nb); + break; + case EvAgain: + peekkey(in, key, 1, &nb); + /* get nb but don't advance */ + break; + default: + ; + } + return ev; +} + +enum KeyEvent +demandkey(Input *in, Key *key) +{ + ulong nb; + enum KeyEvent ev; + + ev = peekkey(in, key, 1, &nb); + if (ev == EvKey) + advance(in, nb); + + return ev; +} + +enum KeyEvent +readkey(Input *in) +{ + int n; + if (in->fd == -1) { + errno = EBADF; + return EvErr; + } + + /* reset to beginning of buffer */ + if (in->buf.b > in->buf.bytes) { + n = in->buf.b - in->buf.bytes; + memmove(in->buf.bytes, in->buf.b, n); + in->buf.b = in->buf.bytes; + in->buf.c = in->buf.b + n; + } + +read: + n = read(in->fd, in->buf.c, in->buf.e-in->buf.c); + if (n == -1) { + if (errno == EAGAIN) + return EvNil; + if (errno == EINTR && !(in->flags & FlagEintr)) + goto read; + else + return EvErr; + } + if (n < 1) { + in->closed = 1; + return EvNil; + } + in->buf.c += n; + return EvAgain; +} + +enum KeyEvent +waitkey(Input *in, Key *key) +{ + enum KeyEvent ev; + struct pollfd p; + + if (in->fd == -1) { + errno = EBADF; + return EvErr; + } + + for (;;) { + ev = getkey(in, key); + switch (ev) { + case EvKey: case EvEOF: case EvErr: + return ev; + case EvNil: + ev = readkey(in); + if (ev == EvErr) + return ev; + break; + + case EvAgain: + /* can't wait any longer */ + if (in->closed) + return demandkey(in, key); + poll: + p.fd = in->fd; + p.events = POLLIN; + + if (poll(&p, 1, in->wait) == -1) { + if (errno == EINTR && !(in->flags & FlagEintr)) + goto poll; + return EvErr; + } + + if (p.revents & (POLLIN|POLLHUP|POLLERR)) + ev = readkey(in); + else + ev = EvNil; + + if (ev == EvErr) + return ev; + if (ev == EvNil) + return demandkey(in, key); + break; + } + } + /* unreachable */ +} + +enum KeyEvent +decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col) +{ + if (key->type != KeyMouse) + return EvNil; + + if (button) + *button = 0; + + getpos(key, row, col); + + if(!ev) + return EvKey; + + int btn = 0; + int code = key->code.mouse[0]; + int drag = code & 0x20; + code &= ~0x3c; + + switch(code) { + case 0: + case 1: + case 2: + *ev = drag ? MouseDrag : MousePress; + btn = code + 1; + break; + + case 3: + *ev = MouseRelease; + // no button hint + break; + + case 64: + case 65: + *ev = drag ? MouseDrag : MousePress; + btn = code + 4 - 64; + break; + + default: + *ev = MouseNil; + } + + if (button) + *button = btn; + + if (key->code.mouse[3] & 0x80) + *ev = MouseRelease; + + return EvKey; +} + +enum KeyEvent +decodepos(Input *in, Key *key, int *row, int *col) +{ + if (key->type != KeyPosition) + return EvNil; + + getpos(key, row, col); + + return EvKey; +} + +enum KeyEvent +decodemode(Input *in, Key *key, int *init, int *mode, int *val) +{ + if (key->type != KeyModeReport) + return EvNil; + + if (init) + *init = key->code.mouse[0]; + + if (mode) + *mode = (key->code.mouse[1] << 8) | key->code.mouse[2]; + + if (val) + *val = key->code.mouse[3]; + + return EvKey; +} + +char * +keyname(Input *in, int sym) +{ + if (sym == SymUnknown || sym >= in->nkeynm) + return ""; + + return in->keynm[sym]; +} + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +printkey(Input *in, Key *key) +{ + enum MouseEvent ev; + int button, line, col; + int init, mode, val; + + switch(key->type) { + case KeyUnicode: + fprintf(stderr, "Unicode codepoint=U+%04dx utf8='%s'", key->code.pt, key->utf8); + break; + case KeyFunc: + fprintf(stderr, "Function F%d", key->code.num); + break; + case KeySym: + fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, keyname(in, key->code.sym)); + break; + case KeyMouse: + decodemouse(in, key, &ev, &button, &line, &col); + fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); + break; + case KeyPosition: + decodepos(in, key, &line, &col); + fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); + break; + case KeyModeReport: + decodemode(in, key, &init, &mode, &val); + fprintf(stderr, "Mode report mode=%s %d val=%d\n", init == '?' ? "DEC" : "ANSI", mode, val); + break; + case KeyDCS: + fprintf(stderr, "Device Control String"); + break; + case KeyOSC: + fprintf(stderr, "Operating System Control"); + break; + case KeyUnknownCSI: + fprintf(stderr, "unknown CSI\n"); + break; + } + + mode = key->mods; + fprintf(stderr, " mod=%s%s%s+%02x", + (mode & ModCtrl ? "" : ""), + (mode & ModAlt ? "" : ""), + (mode & ModShift? "" : ""), + mode & ~(ModCtrl|ModAlt|ModShift)); +} + +int +main() +{ + char *name; + Input *in; + unibi_term *info; + Key key; + enum KeyEvent ev; + + name = getenv("TERM"); + info = unibi_from_term(name); + in = makeinput(0, FlagSpaceSymbol, info); + + int n = 0; /* for debugging purposes only */ + startrecord(in); + while ((ev = waitkey(in, &key)) != EvEOF) { + switch (ev) { + case EvKey: + printkey(in, &key); + printf("\n"); + break; + case EvNil: + printf("\n"); + case EvAgain: + printf("\n"); + default: + ; + } + n++; + if (n > 200) + break; + } + stoprecord(in); + freeinput(in); +} diff --git a/sys/cmd/dvtm/rules.mk b/sys/cmd/dvtm/rules.mk index 6adfd3c..3ef4225 100644 --- a/sys/cmd/dvtm/rules.mk +++ b/sys/cmd/dvtm/rules.mk @@ -2,9 +2,11 @@ include share/push.mk # Local sources SRCS_$(d) := \ -$(d)/hook.c \ -$(d)/vt.c \ -$(d)/dvtm.c +$(d)/events.c \ +# $(d)/driver.c \ +# $(d)/hook.c \ +# $(d)/vt.c \ +# $(d)/dvtm.c # needed for additional editor target AUX := $(d)/dvtm-editor.o @@ -21,7 +23,7 @@ BINS_$(d) := $(DVTM) $(DVTM-ED) include share/paths.mk $(DVTM): TCFLAGS = -D_XOPEN_SOURCE=700 -D_XOPEN_SOURCE_EXTENDED -DNDEBUG -$(DVTM): TCLIBS = -lncursesw -lutil -lc +$(DVTM): TCLIBS = -lunibilium $(OBJ_DIR)/libn/libn.a $(DVTM): $(OBJS_$(d)) $(COMPLINK) diff --git a/sys/cmd/dvtm/term.c b/sys/cmd/dvtm/term.c new file mode 100644 index 0000000..e69de29 diff --git a/sys/cmd/dvtm/term.h b/sys/cmd/dvtm/term.h new file mode 100644 index 0000000..3102458 --- /dev/null +++ b/sys/cmd/dvtm/term.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include + +#define iota(x) 1 << (x) + +typedef struct RGB8 RGB8; +typedef struct Pen Pen; +typedef struct Cell Cell; +typedef struct Row Row; +typedef struct Buffer Buffer; + +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), + /* ... */ + PenTC = iota(15), +}; + +struct Pen +{ + ushort state; + ushort color; + union { + /* 256 color (legacy) */ + struct { + sshort fg : 8, bg : 8; /* 0 - 255 or COLOUR_DEFAULT */ + } col; + /* true color (modern) */ + struct { + RGB8 fg, bg; + } rgb; + }; +}; + +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 *lines; /* 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; + int rows, cols; /* current dimension of buffer */ + int maxcols; /* allocated cells (maximal cols over time) */ +}; + +/* exported functions */ + +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 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); diff --git a/sys/cmd/dvtm/vt.c b/sys/cmd/dvtm/vt.c index c48a84f..efaa980 100644 --- a/sys/cmd/dvtm/vt.c +++ b/sys/cmd/dvtm/vt.c @@ -1,23 +1,15 @@ /* See license for details */ #include +#include -#include -#include -#include -#include -#include -#include #include -#include #include -#include -#include -#include -#include +#include + #include #include -#include -#include + +#include "buffer.h" #if defined(__linux__) || defined(__CYGWIN__) # include @@ -27,602 +19,122 @@ # include #endif -#include "vt.h" - -#ifndef NCURSES_ATTR_SHIFT -# define NCURSES_ATTR_SHIFT 8 -#endif - -#ifndef NCURSES_ACS -# ifdef PDCURSES -# define NCURSES_ACS(c) (acs_map[(uchar)(c)]) -# else /* BSD curses */ -# define NCURSES_ACS(c) (_acs_map[(uchar)(c)]) -# endif -#endif - -#ifdef NCURSES_VERSION - -#ifndef NCURSES_EXT_COLORS -# define NCURSES_EXT_COLORS 0 -#endif - -#if !NCURSES_EXT_COLORS -# define MAX_COLOR_PAIRS MIN(COLOR_PAIRS, 256) -#endif - -#endif - -#ifndef MAX_COLOR_PAIRS -# define MAX_COLOR_PAIRS COLOR_PAIRS -#endif - -#if defined _AIX && defined CTRL -# undef CTRL -#endif -#ifndef CTRL -# define CTRL(k) ((k) & 0x1F) -#endif - #define IS_CONTROL(ch) !((ch) & 0xffffff60UL) -static bool is_utf8, has_default_colors; -static short color_pairs_reserved, color_pairs_max, color_pair_current; -static short *color2palette, default_fg, default_bg; -static char vt_term[32]; - -typedef struct { - wchar_t text; - attr_t attr; - short fg; - short bg; -} Cell; - -typedef struct { - Cell *cells; - uint dirty:1; -} Row; - -/* Buffer holding the current terminal 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 -> - */ -typedef struct { - Row *lines; /* array of Row pointers of size 'rows' */ - Row *curs_row; /* row on which the cursor currently resides */ - Row *scroll_buf; /* a ring buffer holding the scroll back content */ - Row *scroll_top; /* row in lines where scrolling region starts */ - Row *scroll_bot; /* row in lines where scrolling region ends */ - bool *tabs; /* a boolean flag for each column whether it is a tab */ - int scroll_size; /* maximal capacity of scroll back buffer (in lines) */ - int scroll_index; /* current index into the ring buffer */ - int scroll_above; /* number of lines above current viewport */ - int scroll_below; /* number of lines below current viewport */ - int rows, cols; /* current dimension of buffer */ - int maxcols; /* allocated cells (maximal cols over time) */ - attr_t curattrs, savattrs; /* current and saved attributes for cells */ - int curs_col; /* current cursor column (zero based) */ - int curs_srow, curs_scol; /* saved cursor row/colmn (zero based) */ - short curfg, curbg; /* current fore and background colors */ - short savfg, savbg; /* saved colors */ -} Buffer; +typedef struct Vt Vt; struct Vt { - Buffer buffer_normal; /* normal screen buffer */ - Buffer buffer_alternate; /* alternate screen buffer */ - Buffer *buffer; /* currently active buffer (one of the above) */ - attr_t defattrs; /* attributes to use for normal/empty cells */ - short deffg, defbg; /* colors to use for back normal/empty cells (white/black) */ - int pty; /* master side pty file descriptor */ - pid_t pid; /* process id of the process running in this vt */ + Buffer buf[2]; /* normal & alternative screen buffer */ + Buffer *buffer; /* currently active buffer (one of the above) */ + Pen pen; /* default pen */ + int pty; /* master side pty file descriptor */ + pid_t pid; /* process id of the process running in this vt */ /* flags */ - uint seen_input:1; - uint insert:1; - uint escaped:1; - uint curshid:1; - uint curskeymode:1; - uint bell:1; - uint relposmode:1; - uint mousetrack:1; - uint graphmode:1; - uint savgraphmode:1; + char title[256]; /* xterm style window title */ + void *data; /* user supplied data */ + uint seen_input : 1; + uint insert : 1; + uint escaped : 1; + uint curshid : 1; + uint curskeymode : 1; + uint bell : 1; + uint relposmode : 1; + uint mousetrack : 1; + uint graphmode : 1; + uint savgraphmode : 1; bool charsets[2]; /* buffers and parsing state */ char rbuf[BUFSIZ]; char ebuf[BUFSIZ]; uint rlen, elen; - int srow, scol; /* last known offset to display start row, start column */ - char title[256]; /* xterm style window title */ - vt_title_handler_t title_handler; /* hook which is called when title changes */ - vt_urgent_handler_t urgent_handler; /* hook which is called upon bell */ - void *data; /* user supplied data */ -}; - -static const char *keytable[KEY_MAX+1] = { - [KEY_ENTER] = "\r", - ['\n'] = "\n", - /* for the arrow keys the CSI / SS3 sequences are not stored here - * because they depend on the current cursor terminal mode - */ - [KEY_UP] = "A", - [KEY_DOWN] = "B", - [KEY_RIGHT] = "C", - [KEY_LEFT] = "D", -#ifdef KEY_SUP - [KEY_SUP] = "\e[1;2A", -#endif -#ifdef KEY_SDOWN - [KEY_SDOWN] = "\e[1;2B", -#endif - [KEY_SRIGHT] = "\e[1;2C", - [KEY_SLEFT] = "\e[1;2D", - [KEY_BACKSPACE] = "\177", - [KEY_IC] = "\e[2~", - [KEY_DC] = "\e[3~", - [KEY_PPAGE] = "\e[5~", - [KEY_NPAGE] = "\e[6~", - [KEY_HOME] = "\e[7~", - [KEY_END] = "\e[8~", - [KEY_BTAB] = "\e[Z", - [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */ - [KEY_F(1)] = "\e[11~", - [KEY_F(2)] = "\e[12~", - [KEY_F(3)] = "\e[13~", - [KEY_F(4)] = "\e[14~", - [KEY_F(5)] = "\e[15~", - [KEY_F(6)] = "\e[17~", - [KEY_F(7)] = "\e[18~", - [KEY_F(8)] = "\e[19~", - [KEY_F(9)] = "\e[20~", - [KEY_F(10)] = "\e[21~", - [KEY_F(11)] = "\e[23~", - [KEY_F(12)] = "\e[24~", - [KEY_F(13)] = "\e[23~", - [KEY_F(14)] = "\e[24~", - [KEY_F(15)] = "\e[25~", - [KEY_F(16)] = "\e[26~", - [KEY_F(17)] = "\e[28~", - [KEY_F(18)] = "\e[29~", - [KEY_F(19)] = "\e[31~", - [KEY_F(20)] = "\e[32~", - [KEY_F(21)] = "\e[33~", - [KEY_F(22)] = "\e[34~", - [KEY_RESIZE] = "", -#ifdef KEY_EVENT - [KEY_EVENT] = "", -#endif + int srow, scol; /* last known offset to display start row, start column */ }; -static void puttab(Vt *t, int count); -static void process_nonprinting(Vt *t, wchar_t wc); -static void send_curs(Vt *t); - -const static -attr_t -build_attrs(attr_t curattrs) -{ - return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) - >> NCURSES_ATTR_SHIFT; -} - -static -void -row_set(Row *row, int start, int len, Buffer *t) -{ - Cell cell = { - .text = L'\0', - .attr = t ? build_attrs(t->curattrs) : 0, - .fg = t ? t->curfg : -1, - .bg = t ? t->curbg : -1, - }; - - for (int i = start; i < len + start; i++) - row->cells[i] = cell; - row->dirty = true; -} - -static -void -row_roll(Row *start, Row *end, int count) -{ - int n = end - start; - - count %= n; - if (count < 0) - count += n; - - if (count) { - char buf[count * sizeof(Row)]; - memcpy(buf, start, count * sizeof(Row)); - memmove(start, start + count, (n - count) * sizeof(Row)); - memcpy(end - count, buf, count * sizeof(Row)); - for (Row *row = start; row < end; row++) - row->dirty = true; - } -} - -static -void -buffer_clear(Buffer *b) -{ - Cell cell = { - .text = L'\0', - .attr = A_NORMAL, - .fg = -1, - .bg = -1, - }; - - for (int i = 0; i < b->rows; i++) { - Row *row = b->lines + i; - for (int j = 0; j < b->cols; j++) { - row->cells[j] = cell; - row->dirty = true; - } - } -} - -static -void -buffer_free(Buffer *b) -{ - for (int i = 0; i < b->rows; i++) - free(b->lines[i].cells); - free(b->lines); - for (int i = 0; i < b->scroll_size; i++) - free(b->scroll_buf[i].cells); - free(b->scroll_buf); - free(b->tabs); -} - -static -void -buffer_scroll(Buffer *b, int s) -{ - /* work in screenfuls */ - int ssz = b->scroll_bot - b->scroll_top; - if (s > ssz) { - buffer_scroll(b, ssz); - buffer_scroll(b, s - ssz); - return; - } - if (s < -ssz) { - buffer_scroll(b, -ssz); - buffer_scroll(b, s + ssz); - return; - } - - b->scroll_above += s; - if (b->scroll_above >= b->scroll_size) - b->scroll_above = b->scroll_size; - - if (s > 0 && b->scroll_size) { - for (int i = 0; i < s; i++) { - Row tmp = b->scroll_top[i]; - b->scroll_top[i] = b->scroll_buf[b->scroll_index]; - b->scroll_buf[b->scroll_index] = tmp; - - b->scroll_index++; - if (b->scroll_index == b->scroll_size) - b->scroll_index = 0; - } - } - row_roll(b->scroll_top, b->scroll_bot, s); - if (s < 0 && b->scroll_size) { - for (int i = (-s) - 1; i >= 0; i--) { - b->scroll_index--; - if (b->scroll_index == -1) - b->scroll_index = b->scroll_size - 1; - - Row tmp = b->scroll_top[i]; - b->scroll_top[i] = b->scroll_buf[b->scroll_index]; - b->scroll_buf[b->scroll_index] = tmp; - b->scroll_top[i].dirty = true; - } - } -} - -static -void -buffer_resize(Buffer *b, int rows, int cols) -{ - Row *lines = b->lines; - - if (b->rows != rows) { - if (b->curs_row >= lines + rows) { - /* scroll up instead of simply chopping off bottom */ - buffer_scroll(b, (b->curs_row - b->lines) - rows + 1); - } - while (b->rows > rows) { - free(lines[b->rows - 1].cells); - b->rows--; - } - - lines = realloc(lines, sizeof(Row) * rows); - } - - if (b->maxcols < cols) { - for (int row = 0; row < b->rows; row++) { - lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols); - if (b->cols < cols) - row_set(lines + row, b->cols, cols - b->cols, nil); - lines[row].dirty = true; - } - Row *sbuf = b->scroll_buf; - for (int row = 0; row < b->scroll_size; row++) { - sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols); - if (b->cols < cols) - row_set(sbuf + row, b->cols, cols - b->cols, nil); - } - b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols); - for (int col = b->cols; col < cols; col++) - b->tabs[col] = !(col & 7); - b->maxcols = cols; - b->cols = cols; - } else if (b->cols != cols) { - for (int row = 0; row < b->rows; row++) - lines[row].dirty = true; - b->cols = cols; - } - - int deltarows = 0; - if (b->rows < rows) { - while (b->rows < rows) { - lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell)); - row_set(lines + b->rows, 0, b->maxcols, b); - b->rows++; - } - - /* prepare for backfill */ - if (b->curs_row >= b->scroll_bot - 1) { - deltarows = b->lines + rows - b->curs_row - 1; - if (deltarows > b->scroll_above) - deltarows = b->scroll_above; - } - } +/* forward declares */ - b->curs_row += lines - b->lines; - b->scroll_top = lines; - b->scroll_bot = lines + rows; - b->lines = lines; +void vt·noscroll(Vt *t); +void vt·dirty(Vt *t); +void vt·scroll(Vt *t, int rows); - /* perform backfill */ - if (deltarows > 0) { - buffer_scroll(b, -deltarows); - b->curs_row += deltarows; - } -} +static void puttab(Vt *t, int count); +static void process_nonprinting(Vt *t, rune r); +static void sendcurs(Vt *t); -static -bool -buffer_init(Buffer *b, int rows, int cols, int scroll_size) -{ - b->curattrs = A_NORMAL; /* white text over black background */ - b->curfg = b->curbg = -1; - if (scroll_size < 0) - scroll_size = 0; - if (scroll_size && !(b->scroll_buf = calloc(scroll_size, sizeof(Row)))) - return false; - b->scroll_size = scroll_size; - buffer_resize(b, rows, cols); - return true; -} +/* globals */ +static int isutf8; +static char vtname[36]; static void -buffer_boundry(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) -{ - if (bs) - *bs = nil; - if (be) - *be = nil; - if (as) - *as = nil; - if (ae) - *ae = nil; - if (!b->scroll_size) - return; - - if (b->scroll_above) { - if (bs) - *bs = &b->scroll_buf[(b->scroll_index - b->scroll_above + b->scroll_size) % b->scroll_size]; - if (be) - *be = &b->scroll_buf[(b->scroll_index-1 + b->scroll_size) % b->scroll_size]; - } - if (b->scroll_below) { - if (as) - *as = &b->scroll_buf[b->scroll_index]; - if (ae) - *ae = &b->scroll_buf[(b->scroll_index + b->scroll_below-1) % b->scroll_size]; - } -} - -static -Row * -buffer_row_first(Buffer *b) { - Row *bstart; - if (!b->scroll_size || !b->scroll_above) - return b->lines; - buffer_boundry(b, &bstart, nil, nil, nil); - return bstart; -} - -static -Row * -buffer_row_last(Buffer *b) { - Row *aend; - if (!b->scroll_size || !b->scroll_below) - return b->lines + b->rows - 1; - buffer_boundry(b, nil, nil, nil, &aend); - return aend; -} - -static -Row * -buffer_row_next(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->lines, *last = b->lines + b->rows - 1; - - if (!row) - return nil; - - buffer_boundry(b, &before_start, &before_end, &after_start, &after_end); - - if (row >= first && row < last) - return ++row; - if (row == last) - return after_start; - if (row == before_end) - return first; - if (row == after_end) - return nil; - if (row == &b->scroll_buf[b->scroll_size - 1]) - return b->scroll_buf; - return ++row; -} - -static -Row * -buffer_row_prev(Buffer *b, Row *row) +cclamp(Vt *t) { - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->lines, *last = b->lines + b->rows - 1; - - if (!row) - return nil; - - buffer_boundry(b, &before_start, &before_end, &after_start, &after_end); + Buffer *b = t->buffer; + Row *lines = t->relposmode ? b->scroll.top : b->lines; + int rows = t->relposmode ? b->scroll.bot - b->scroll.top : b->rows; - if (row > first && row <= last) - return --row; - if (row == first) - return before_end; - if (row == before_start) - return nil; - if (row == after_start) - return last; - if (row == b->scroll_buf) - return &b->scroll_buf[b->scroll_size - 1]; - return --row; + if (b->crow < lines) + b->crow = lines; + if (b->crow >= lines + rows) + b->crow = lines + rows - 1; + if (b->ccol < 0) + b->ccol = 0; + if (b->ccol >= b->cols) + b->ccol = b->cols - 1; } static void -cursor_clamp(Vt *t) +clinedown(Vt *t) { Buffer *b = t->buffer; - Row *lines = t->relposmode ? b->scroll_top : b->lines; - int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows; - - if (b->curs_row < lines) - b->curs_row = lines; - if (b->curs_row >= lines + rows) - b->curs_row = lines + rows - 1; - if (b->curs_col < 0) - b->curs_col = 0; - if (b->curs_col >= b->cols) - b->curs_col = b->cols - 1; -} - -static -void -cursor_line_down(Vt *t) -{ - Buffer *b = t->buffer; - row_set(b->curs_row, b->cols, b->maxcols - b->cols, nil); - b->curs_row++; - if (b->curs_row < b->scroll_bot) + zero(b->crow, b->cols, b->maxcols - b->cols); + b->crow++; + if (b->crow < b->scroll.bot) return; - vt_noscroll(t); + vt·noscroll(t); - b->curs_row = b->scroll_bot - 1; - buffer_scroll(b, 1); - row_set(b->curs_row, 0, b->cols, b); + b->crow = b->scroll.bot - 1; + bscroll(b, 1); + zero(b->crow, 0, b->cols); } static void -cursor_save(Vt *t) +csave(Vt *t) { Buffer *b = t->buffer; - b->curs_srow = b->curs_row - b->lines; - b->curs_scol = b->curs_col; + b->scrow = b->crow - b->lines; + b->sccol = b->ccol; } static void -cursor_restore(Vt *t) +crestore(Vt *t) { Buffer *b = t->buffer; - b->curs_row = b->lines + b->curs_srow; - b->curs_col = b->curs_scol; - cursor_clamp(t); + b->crow = b->lines + b->scrow; + b->ccol = b->sccol; + cclamp(t); } static void -attributes_save(Vt *t) +savepen(Vt *t) { Buffer *b = t->buffer; - b->savattrs = b->curattrs; - b->savfg = b->curfg; - b->savbg = b->curbg; - t->savgraphmode = t->graphmode; + b->spen = b->pen; } static void -attributes_restore(Vt *t) +loadpen(Vt *t) { Buffer *b = t->buffer; - b->curattrs = b->savattrs; - b->curfg = b->savfg; - b->curbg = b->savbg; - t->graphmode = t->savgraphmode; + b->pen = b->spen; } + static void new_escape_sequence(Vt *t) @@ -658,89 +170,85 @@ interpret_csi_sgr(Vt *t, int param[], int pcount) Buffer *b = t->buffer; if (pcount == 0) { /* special case: reset attributes */ - b->curattrs = A_NORMAL; - b->curfg = b->curbg = -1; + b->pen.state = PenNormal; + b->pen.col.fg = b->pen.col.bg = -1; return; } for (int i = 0; i < pcount; i++) { switch (param[i]) { case 0: - b->curattrs = A_NORMAL; - b->curfg = b->curbg = -1; + b->pen.state = PenNormal; + b->pen.col.fg = b->pen.col.bg = -1; break; case 1: - b->curattrs |= A_BOLD; + b->pen.state |= PenBold; break; case 2: - b->curattrs |= A_DIM; + b->pen.state |= PenDim; break; -#ifdef A_ITALIC case 3: - b->curattrs |= A_ITALIC; + b->pen.state |= PenItalic; break; -#endif case 4: - b->curattrs |= A_UNDERLINE; + b->pen.state |= PenUnderline; break; case 5: - b->curattrs |= A_BLINK; + b->pen.state |= PenBlink; break; case 7: - b->curattrs |= A_REVERSE; + b->pen.state |= PenReverse; break; case 8: - b->curattrs |= A_INVIS; + b->pen.state |= PenInvis; break; case 22: - b->curattrs &= ~(A_BOLD | A_DIM); + b->pen.state &= ~(PenBold | PenDim); break; -#ifdef A_ITALIC case 23: - b->curattrs &= ~A_ITALIC; + b->pen.state &= ~PenItalic; break; -#endif case 24: - b->curattrs &= ~A_UNDERLINE; + b->pen.state &= ~PenUnderline; break; case 25: - b->curattrs &= ~A_BLINK; + b->pen.state &= ~PenBlink; break; case 27: - b->curattrs &= ~A_REVERSE; + b->pen.state &= ~PenReverse; break; case 28: - b->curattrs &= ~A_INVIS; + b->pen.state &= ~PenInvis; break; case 30 ... 37: /* fg */ - b->curfg = param[i] - 30; + b->pen.col.fg = param[i] - 30; break; case 38: if ((i + 2) < pcount && param[i + 1] == 5) { - b->curfg = param[i + 2]; + b->pen.col.fg = param[i + 2]; i += 2; } break; case 39: - b->curfg = -1; + b->pen.col.fg = -1; break; case 40 ... 47: /* bg */ - b->curbg = param[i] - 40; + b->pen.col.bg = param[i] - 40; break; case 48: if ((i + 2) < pcount && param[i + 1] == 5) { - b->curbg = param[i + 2]; + b->pen.col.bg = param[i + 2]; i += 2; } break; case 49: - b->curbg = -1; + b->pen.col.bg = -1; break; - case 90 ... 97: /* hi fg */ - b->curfg = param[i] - 82; + case 90 ... 97: /* hi fg */ + b->pen.col.fg = param[i] - 82; break; case 100 ... 107: /* hi bg */ - b->curbg = param[i] - 92; + b->pen.col.bg = param[i] - 92; break; default: break; @@ -756,27 +264,27 @@ interpret_csi_ed(Vt *t, int param[], int pcount) Row *row, *start, *end; Buffer *b = t->buffer; - attributes_save(t); - b->curattrs = A_NORMAL; - b->curfg = b->curbg = -1; + savepen(t); + b->pen.state = PenNormal; + b->pen.col.fg = b->pen.col.bg = -1; if (pcount && param[0] == 2) { start = b->lines; - end = b->lines + b->rows; + end = b->lines + b->rows; } else if (pcount && param[0] == 1) { start = b->lines; - end = b->curs_row; - row_set(b->curs_row, 0, b->curs_col + 1, b); + end = b->crow; + zero(b->crow, 0, b->ccol + 1); } else { - row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); - start = b->curs_row + 1; - end = b->lines + b->rows; + zero(b->crow, b->ccol, b->cols - b->ccol); + start = b->crow + 1; + end = b->lines + b->rows; } for (row = start; row < end; row++) - row_set(row, 0, b->cols, b); + zero(row, 0, b->cols); - attributes_restore(t); + loadpen(t); } /* interprets a 'move cursor' (CUP) escape sequence */ @@ -784,21 +292,21 @@ static void interpret_csi_cup(Vt *t, int param[], int pcount) { - Buffer *b = t->buffer; - Row *lines = t->relposmode ? b->scroll_top : b->lines; + Buffer *b = t->buffer; + Row *lines = t->relposmode ? b->scroll.top : b->lines; if (pcount == 0) { - b->curs_row = lines; - b->curs_col = 0; + b->crow = lines; + b->ccol = 0; } else if (pcount == 1) { - b->curs_row = lines + param[0] - 1; - b->curs_col = 0; + b->crow = lines + param[0] - 1; + b->ccol = 0; } else { - b->curs_row = lines + param[0] - 1; - b->curs_col = param[1] - 1; + b->crow = lines + param[0] - 1; + b->ccol = param[1] - 1; } - cursor_clamp(t); + cclamp(t); } /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, @@ -812,37 +320,37 @@ interpret_csi_c(Vt *t, char verb, int param[], int pcount) switch (verb) { case 'A': - b->curs_row -= n; + b->crow -= n; break; case 'B': case 'e': - b->curs_row += n; + b->crow += n; break; case 'C': case 'a': - b->curs_col += n; + b->ccol += n; break; case 'D': - b->curs_col -= n; + b->ccol -= n; break; case 'E': - b->curs_row += n; - b->curs_col = 0; + b->crow += n; + b->ccol = 0; break; case 'F': - b->curs_row -= n; - b->curs_col = 0; + b->crow -= n; + b->ccol = 0; break; case 'G': case '`': - b->curs_col = n - 1; + b->ccol = n - 1; break; case 'd': - b->curs_row = b->lines + n - 1; + b->crow = b->lines + n - 1; break; } - cursor_clamp(t); + cclamp(t); } /* Interpret the 'erase line' escape sequence */ @@ -853,13 +361,13 @@ interpret_csi_el(Vt *t, int param[], int pcount) Buffer *b = t->buffer; switch (pcount ? param[0] : 0) { case 1: - row_set(b->curs_row, 0, b->curs_col + 1, b); + zero(b->crow, 0, b->ccol + 1); break; case 2: - row_set(b->curs_row, 0, b->cols, b); + zero(b->crow, 0, b->cols); break; default: - row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); + zero(b->crow, b->ccol, b->cols - b->ccol); break; } } @@ -870,16 +378,16 @@ void interpret_csi_ich(Vt *t, int param[], int pcount) { Buffer *b = t->buffer; - Row *row = b->curs_row; + Row *row = b->crow; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->curs_col + n > b->cols) - n = b->cols - b->curs_col; + if (b->ccol + n > b->cols) + n = b->cols - b->ccol; - for (int i = b->cols - 1; i >= b->curs_col + n; i--) + for (int i = b->cols - 1; i >= b->ccol + n; i--) row->cells[i] = row->cells[i - n]; - row_set(row, b->curs_col, n, b); + zero(row, b->ccol, n); } /* Interpret the 'delete chars' sequence (DCH) */ @@ -888,16 +396,16 @@ void interpret_csi_dch(Vt *t, int param[], int pcount) { Buffer *b = t->buffer; - Row *row = b->curs_row; + Row *row = b->crow; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->curs_col + n > b->cols) - n = b->cols - b->curs_col; + if (b->ccol + n > b->cols) + n = b->cols - b->ccol; - for (int i = b->curs_col; i < b->cols - n; i++) + for (int i = b->ccol; i < b->cols - n; i++) row->cells[i] = row->cells[i + n]; - row_set(row, b->cols - n, n, b); + zero(row, b->cols - n, n); } /* Interpret an 'insert line' sequence (IL) */ @@ -908,13 +416,13 @@ interpret_csi_il(Vt *t, int param[], int pcount) Buffer *b = t->buffer; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->curs_row + n >= b->scroll_bot) { - for (Row *row = b->curs_row; row < b->scroll_bot; row++) - row_set(row, 0, b->cols, b); + if (b->crow + n >= b->scroll.bot) { + for (Row *row = b->crow; row < b->scroll.bot; row++) + zero(row, 0, b->cols); } else { - row_roll(b->curs_row, b->scroll_bot, -n); - for (Row *row = b->curs_row; row < b->curs_row + n; row++) - row_set(row, 0, b->cols, b); + roll(b->crow, b->scroll.bot, -n); + for (Row *row = b->crow; row < b->crow + n; row++) + zero(row, 0, b->cols); } } @@ -926,13 +434,13 @@ interpret_csi_dl(Vt *t, int param[], int pcount) Buffer *b = t->buffer; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->curs_row + n >= b->scroll_bot) { - for (Row *row = b->curs_row; row < b->scroll_bot; row++) - row_set(row, 0, b->cols, b); + if (b->crow + n >= b->scroll.bot) { + for (Row *row = b->crow; row < b->scroll.bot; row++) + zero(row, 0, b->cols); } else { - row_roll(b->curs_row, b->scroll_bot, n); - for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++) - row_set(row, 0, b->cols, b); + roll(b->crow, b->scroll.bot, n); + for (Row *row = b->scroll.bot - n; row < b->scroll.bot; row++) + zero(row, 0, b->cols); } } @@ -944,10 +452,10 @@ interpret_csi_ech(Vt *t, int param[], int pcount) Buffer *b = t->buffer; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->curs_col + n > b->cols) - n = b->cols - b->curs_col; + if (b->ccol + n > b->cols) + n = b->cols - b->ccol; - row_set(b->curs_row, b->curs_col, n, b); + zero(b->crow, b->ccol, n); } /* Interpret a 'set scrolling region' (DECSTBM) sequence */ @@ -960,8 +468,8 @@ interpret_csi_decstbm(Vt *t, int param[], int pcount) switch (pcount) { case 0: - b->scroll_top = b->lines; - b->scroll_bot = b->lines + b->rows; + b->scroll.top = b->lines; + b->scroll.bot = b->lines + b->rows; break; case 2: new_top = param[0] - 1; @@ -979,15 +487,15 @@ interpret_csi_decstbm(Vt *t, int param[], int pcount) /* check for range validity */ if (new_top < new_bot) { - b->scroll_top = b->lines + new_top; - b->scroll_bot = b->lines + new_bot; + b->scroll.top = b->lines + new_top; + b->scroll.bot = b->lines + new_bot; } break; default: return; /* malformed */ } - b->curs_row = b->scroll_top; - b->curs_col = 0; + b->crow = b->scroll.top; + b->ccol = 0; } static @@ -1022,17 +530,17 @@ interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set) case 47: /* use alternate/normal screen buffer */ case 1047: if (!set) - buffer_clear(&t->buffer_alternate); - t->buffer = set ? &t->buffer_alternate : &t->buffer_normal; - vt_dirty(t); + bclear(&t->buf[1]); + t->buffer = set ? &t->buf[1] : &t->buf[0]; + vt·dirty(t); if (param[i] != 1049) break; /* fall through */ case 1048: /* save/restore cursor */ if (set) - cursor_save(t); + csave(t); else - cursor_restore(t); + crestore(t); break; case 1000: /* enable/disable normal mouse tracking */ t->mousetrack = set; @@ -1125,10 +633,10 @@ interpret_csi(Vt *t) interpret_csi_ech(t, csiparam, param_count); break; case 'S': /* SU: scroll up */ - vt_scroll(t, param_count ? -csiparam[0] : -1); + vt·scroll(t, param_count ? -csiparam[0] : -1); break; case 'T': /* SD: scroll down */ - vt_scroll(t, param_count ? csiparam[0] : 1); + vt·scroll(t, param_count ? csiparam[0] : 1); break; case 'Z': /* CBT: cursor backward tabulation */ puttab(t, param_count ? -csiparam[0] : -1); @@ -1136,7 +644,7 @@ interpret_csi(Vt *t) case 'g': /* TBC: tabulation clear */ switch (param_count ? csiparam[0] : 0) { case 0: - b->tabs[b->curs_col] = false; + b->tabs[b->ccol] = false; break; case 3: memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols); @@ -1147,14 +655,14 @@ interpret_csi(Vt *t) interpret_csi_decstbm(t, csiparam, param_count); break; case 's': /* save cursor location */ - cursor_save(t); + csave(t); break; case 'u': /* restore cursor location */ - cursor_restore(t); + crestore(t); break; case 'n': /* query cursor location */ if (param_count == 1 && csiparam[0] == 6) - send_curs(t); + sendcurs(t); break; default: break; @@ -1167,8 +675,8 @@ void interpret_csi_ind(Vt *t) { Buffer *b = t->buffer; - if (b->curs_row < b->lines + b->rows - 1) - b->curs_row++; + if (b->crow < b->lines + b->rows - 1) + b->crow++; } /* Interpret a 'reverse index' (RI) sequence */ @@ -1177,11 +685,11 @@ void interpret_csi_ri(Vt *t) { Buffer *b = t->buffer; - if (b->curs_row > b->scroll_top) - b->curs_row--; + if (b->crow > b->scroll.top) + b->crow--; else { - row_roll(b->scroll_top, b->scroll_bot, -1); - row_set(b->scroll_top, 0, b->cols, b); + roll(b->scroll.top, b->scroll.bot, -1); + zero(b->scroll.top, 0, b->cols); } } @@ -1191,9 +699,9 @@ void interpret_csi_nel(Vt *t) { Buffer *b = t->buffer; - if (b->curs_row < b->lines + b->rows - 1) { - b->curs_row++; - b->curs_col = 0; + if (b->crow < b->lines + b->rows - 1) { + b->crow++; + b->ccol = 0; } } @@ -1222,8 +730,8 @@ interpret_osc(Vt *t) switch (command) { case 0: /* icon name and window title */ case 2: /* window title */ - if (t->title_handler) - t->title_handler(t, data+1); + // if (t->title_handler) + // t->title_handler(t, data+1); break; case 1: /* icon name */ break; @@ -1278,12 +786,12 @@ try_interpret_escape_seq(Vt *t) } break; case '7': /* DECSC: save cursor and attributes */ - attributes_save(t); - cursor_save(t); + savepen(t); + csave(t); goto handled; case '8': /* DECRC: restore cursor and attributes */ - attributes_restore(t); - cursor_restore(t); + loadpen(t); + crestore(t); goto handled; case 'D': /* IND: index */ interpret_csi_ind(t); @@ -1295,7 +803,7 @@ try_interpret_escape_seq(Vt *t) interpret_csi_nel(t); goto handled; case 'H': /* HTS: horizontal tab set */ - t->buffer->tabs[t->buffer->curs_col] = true; + t->buffer->tabs[t->buffer->ccol] = true; goto handled; default: goto cancel; @@ -1325,17 +833,17 @@ puttab(Vt *t, int count) { Buffer *b = t->buffer; int direction = count >= 0 ? 1 : -1; - for (int col = b->curs_col + direction; count; col += direction) { + for (int col = b->ccol + direction; count; col += direction) { if (col < 0) { - b->curs_col = 0; + b->ccol = 0; break; } if (col >= b->cols) { - b->curs_col = b->cols - 1; + b->ccol = b->cols - 1; break; } if (b->tabs[col]) { - b->curs_col = col; + b->ccol = col; count -= direction; } } @@ -1343,31 +851,31 @@ puttab(Vt *t, int count) static void -process_nonprinting(Vt *t, wchar_t wc) +process_nonprinting(Vt *t, rune r) { Buffer *b = t->buffer; - switch (wc) { + switch (r) { case '\e': /* ESC */ new_escape_sequence(t); break; case '\a': /* BEL */ - if (t->urgent_handler) - t->urgent_handler(t); + // if (t->urgent_handler) + // t->urgent_handler(t); break; case '\b': /* BS */ - if (b->curs_col > 0) - b->curs_col--; + if (b->ccol > 0) + b->ccol--; break; case '\t': /* HT */ puttab(t, 1); break; case '\r': /* CR */ - b->curs_col = 0; + b->ccol = 0; break; case '\v': /* VT */ case '\f': /* FF */ case '\n': /* LF */ - cursor_line_down(t); + clinedown(t); break; case '\016': /* SO: shift out, invoke the G1 character set */ t->graphmode = t->charsets[1]; @@ -1385,7 +893,7 @@ is_utf8_locale(void) const char *cset = nl_langinfo(CODESET); if (!cset) cset = "ANSI_X3.4-1968"; - is_utf8 = !strcmp(cset, "UTF-8"); + isutf8 = !strcmp(cset, "UTF-8"); } static @@ -1409,17 +917,18 @@ get_vt100_graphic(char c) 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e }; - if (is_utf8) + if (isutf8) return vt100_utf8[c - 0x41]; - else if (strchr(vt100_acs, c)) - return NCURSES_ACS(c); + // else if (strchr(vt100_acs, c)) + // return NCURSES_ACS(c); return '\0'; } static void -put_wc(Vt *t, wchar_t wc) +putrune(Vt *t, rune r) { + Cell blank; int width = 0; if (!t->seen_input) { @@ -1429,54 +938,61 @@ put_wc(Vt *t, wchar_t wc) if (t->escaped) { if (t->elen + 1 < sizeof(t->ebuf)) { - t->ebuf[t->elen] = wc; + t->ebuf[t->elen] = r; t->ebuf[++t->elen] = '\0'; try_interpret_escape_seq(t); } else { cancel_escape_sequence(t); } - } else if (IS_CONTROL(wc)) { - process_nonprinting(t, wc); + } else if (IS_CONTROL(r)) { + process_nonprinting(t, r); } else { if (t->graphmode) { - if (wc >= 0x41 && wc <= 0x7e) { - wchar_t gc = get_vt100_graphic(wc); + if (r >= 0x41 && r <= 0x7e) { + wchar_t gc = get_vt100_graphic(r); if (gc) - wc = gc; + r = gc; } width = 1; - } else if ((width = wcwidth(wc)) < 1) { + } else if ((width = wcwidth(r)) < 1) { width = 1; } Buffer *b = t->buffer; - Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg }; - if (width == 2 && b->curs_col == b->cols - 1) { - b->curs_row->cells[b->curs_col++] = blank_cell; - b->curs_row->dirty = true; + blank = (Cell){ + .r = L'\0', + .pen = (Pen){ + .state = b->pen.state, + .col = b->pen.col + }, + }; + if (width == 2 && b->ccol == b->cols - 1) { + b->crow->cells[b->ccol++] = blank; + b->crow->dirty = true; } - if (b->curs_col >= b->cols) { - b->curs_col = 0; - cursor_line_down(t); + if (b->ccol >= b->cols) { + b->ccol = 0; + clinedown(t); } if (t->insert) { - Cell *src = b->curs_row->cells + b->curs_col; + Cell *src = b->crow->cells + b->ccol; Cell *dest = src + width; - size_t len = b->cols - b->curs_col - width; + size_t len = b->cols - b->ccol - width; memmove(dest, src, len * sizeof *dest); } - b->curs_row->cells[b->curs_col] = blank_cell; - b->curs_row->cells[b->curs_col++].text = wc; - b->curs_row->dirty = true; + b->crow->cells[b->ccol] = blank; + b->crow->cells[b->ccol++].r = r; + b->crow->dirty = true; + if (width == 2) - b->curs_row->cells[b->curs_col++] = blank_cell; + b->crow->cells[b->ccol++] = blank; } } int -vt_process(Vt *t) +vt·process(Vt *t) { int res; uint pos = 0; @@ -1494,10 +1010,11 @@ vt_process(Vt *t) t->rlen += res; while (pos < t->rlen) { - wchar_t wc; - ssize_t len; + rune r; + size_t len; - len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps); + // XXX: convert this to use utf8 functions + len = (ssize_t)mbrtowc((wchar*)&r, t->rbuf + pos, t->rlen - pos, &ps); if (len == -2) { t->rlen -= pos; memmove(t->rbuf, t->rbuf + pos, t->rlen); @@ -1506,11 +1023,11 @@ vt_process(Vt *t) if (len == -1) { len = 1; - wc = t->rbuf[pos]; + r = t->rbuf[pos]; } pos += len ? len : 1; - put_wc(t, wc); + putrune(t, r); } t->rlen -= pos; @@ -1518,16 +1035,9 @@ vt_process(Vt *t) return 0; } -void -vt_default_colors_set(Vt *t, attr_t attrs, short fg, short bg) -{ - t->defattrs = attrs; - t->deffg = fg; - t->defbg = bg; -} - +/* size is the number of rows kept in the scrollback */ Vt * -vt_create(int rows, int cols, int scroll_size) +vt·make(int rows, int cols, int size) { if (rows <= 0 || cols <= 0) return nil; @@ -1537,11 +1047,14 @@ vt_create(int rows, int cols, int scroll_size) return nil; t->pty = -1; - t->deffg = t->defbg = -1; - t->buffer = &t->buffer_normal; - - if (!buffer_init(&t->buffer_normal, rows, cols, scroll_size) || - !buffer_init(&t->buffer_alternate, rows, cols, 0)) { + t->pen = (Pen) { + .state = PenNormal, + .col = {-1, -1}, + }; + t->buffer = &t->buf[0]; + + if (!binit(&t->buf[0], rows, cols, size) || + !binit(&t->buf[1], rows, cols, 0)) { free(t); return nil; } @@ -1550,34 +1063,34 @@ vt_create(int rows, int cols, int scroll_size) } void -vt_resize(Vt *t, int rows, int cols) +vt·resize(Vt *t, int rows, int cols) { struct winsize ws = { .ws_row = rows, .ws_col = cols }; if (rows <= 0 || cols <= 0) return; - vt_noscroll(t); - buffer_resize(&t->buffer_normal, rows, cols); - buffer_resize(&t->buffer_alternate, rows, cols); - cursor_clamp(t); + vt·noscroll(t); + bresize(&t->buf[0], rows, cols); + bresize(&t->buf[1], rows, cols); + cclamp(t); ioctl(t->pty, TIOCSWINSZ, &ws); kill(-t->pid, SIGWINCH); } void -vt_destroy(Vt *t) +vt·free(Vt *t) { if (!t) return; - buffer_free(&t->buffer_normal); - buffer_free(&t->buffer_alternate); + bfree(&t->buf[0]); + bfree(&t->buf[1]); close(t->pty); free(t); } void -vt_dirty(Vt *t) +vt·dirty(Vt *t) { Buffer *b = t->buffer; for (Row *row = b->lines, *end = row + b->rows; row < end; row++) @@ -1585,51 +1098,49 @@ vt_dirty(Vt *t) } void -vt_draw(Vt *t, WINDOW *win, int srow, int scol) +vt·draw(Vt *t, Window *win, int srow, int scol) { + int i, j; + Cell *cell, *prev; Buffer *b = t->buffer; if (srow != t->srow || scol != t->scol) { - vt_dirty(t); + vt·dirty(t); t->srow = srow; t->scol = scol; } - for (int i = 0; i < b->rows; i++) { + for (i = 0; i < b->rows; i++) { Row *row = b->lines + i; if (!row->dirty) continue; wmove(win, srow + i, scol); - Cell *cell = nil; - for (int j = 0; j < b->cols; j++) { - Cell *prev_cell = cell; + for (j = 0; j < b->cols; j++) { + prev = cell; cell = row->cells + j; - if (!prev_cell || cell->attr != prev_cell->attr - || cell->fg != prev_cell->fg - || cell->bg != prev_cell->bg) { - if (cell->attr == A_NORMAL) - cell->attr = t->defattrs; - if (cell->fg == -1) - cell->fg = t->deffg; - if (cell->bg == -1) - cell->bg = t->defbg; - wattrset(win, cell->attr << NCURSES_ATTR_SHIFT); - wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil); + if (!prev || !peneq(cell->pen, prev->pen)) { + if (cell->pen.state == PenNormal) + cell->pen.state = t->pen.state; + if (cell->pen.col.fg == -1) + cell->pen.col.fg = t->pen.col.fg; + if (cell->pen.col.bg == -1) + cell->pen.col.bg = t->pen.col.bg; + // wattrset(win, cell->attr << NCURSES_ATTR_SHIFT); + // wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil); } - if (is_utf8 && cell->text >= 128) { + if (isutf8 && cell->r >= RuneSync) { char buf[MB_CUR_MAX + 1]; - size_t len = wcrtomb(buf, cell->text, nil); + size_t len = wcrtomb(buf, cell->r, nil); if (len > 0) { waddnstr(win, buf, len); - if (wcwidth(cell->text) > 1) + if (wcwidth(cell->r) > 1) j++; } - } else { - waddch(win, cell->text > ' ' ? cell->text : ' '); - } + } else + waddch(win, cell->r > ' ' ? cell->r: ' '); } int x, y; @@ -1641,36 +1152,36 @@ vt_draw(Vt *t, WINDOW *win, int srow, int scol) row->dirty = false; } - wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col); + wmove(win, srow + b->crow - b->lines, scol + b->ccol); } void -vt_scroll(Vt *t, int rows) +vt·scroll(Vt *t, int rows) { Buffer *b = t->buffer; - if (!b->scroll_size) + if (!b->scroll.size) return; if (rows < 0) { /* scroll back */ - if (rows < -b->scroll_above) - rows = -b->scroll_above; + if (rows < -b->scroll.above) + rows = -b->scroll.above; } else { /* scroll forward */ - if (rows > b->scroll_below) - rows = b->scroll_below; + if (rows > b->scroll.below) + rows = b->scroll.below; } - buffer_scroll(b, rows); - b->scroll_below -= rows; + bscroll(b, rows); + b->scroll.below -= rows; } void -vt_noscroll(Vt *t) +vt·noscroll(Vt *t) { - int scroll_below = t->buffer->scroll_below; - if (scroll_below) - vt_scroll(t, scroll_below); + int below = t->buffer->scroll.below; + if (below) + vt·scroll(t, below); } pid_t -vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from) +vt·forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from) { int vt2ed[2], ed2vt[2]; struct winsize ws; @@ -1717,7 +1228,7 @@ vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char for (const char **envp = env; envp && envp[0]; envp += 2) setenv(envp[0], envp[1], 1); - setenv("TERM", vt_term, 1); + setenv("TERM", vtname, 1); if (cwd) chdir(cwd); @@ -1746,13 +1257,13 @@ vt_pty_get(Vt *t) return t->pty; } -ssize_t -vt_write(Vt *t, const char *buf, size_t len) +uintptr +vt·write(Vt *t, const char *buf, size_t len) { - ssize_t ret = len; + uintptr res, ret = len; while (len > 0) { - ssize_t res = write(t->pty, buf, len); + res = write(t->pty, buf, len); if (res < 0) { if (errno != EAGAIN && errno != EINTR) return -1; @@ -1767,18 +1278,19 @@ vt_write(Vt *t, const char *buf, size_t len) static void -send_curs(Vt *t) +sendcurs(Vt *t) { Buffer *b = t->buffer; char keyseq[16]; - snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col); - vt_write(t, keyseq, strlen(keyseq)); + snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->crow - b->lines), b->ccol); + vt·write(t, keyseq, strlen(keyseq)); } +#if 0 void -vt_keypress(Vt *t, int keycode) +vt·keypress(Vt *t, int keycode) { - vt_noscroll(t); + vt·noscroll(t); if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) { switch (keycode) { @@ -1787,26 +1299,23 @@ vt_keypress(Vt *t, int keycode) case KEY_RIGHT: case KEY_LEFT: { char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] }; - vt_write(t, keyseq, sizeof keyseq); + vt·write(t, keyseq, sizeof keyseq); break; } default: - vt_write(t, keytable[keycode], strlen(keytable[keycode])); + vt·write(t, keytable[keycode], strlen(keytable[keycode])); } } else if (keycode <= UCHAR_MAX) { char c = keycode; - vt_write(t, &c, 1); + vt·write(t, &c, 1); } else { -#ifndef NDEBUG fprintf(stderr, "unhandled key %#o\n", keycode); -#endif } } void -vt_mouse(Vt *t, int x, int y, mmask_t mask) +vt·mouse(Vt *t, int x, int y, mmask_t mask) { -#ifdef NCURSES_MOUSE_VERSION char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0; if (!t->mousetrack) @@ -1832,16 +1341,16 @@ vt_mouse(Vt *t, int x, int y, mmask_t mask) seq[4] = 32 + x; seq[5] = 32 + y; - vt_write(t, seq, sizeof seq); + vt·write(t, seq, sizeof seq); if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) { /* send a button release event */ button = 3; seq[3] = 32 + button + state; - vt_write(t, seq, sizeof seq); + vt·write(t, seq, sizeof seq); } -#endif /* NCURSES_MOUSE_VERSION */ } +#endif static uint @@ -1854,102 +1363,26 @@ color_hash(short fg, short bg) return fg * (COLORS + 2) + bg; } -short -vt_color_get(Vt *t, short fg, short bg) -{ - if (fg >= COLORS) - fg = (t ? t->deffg : default_fg); - if (bg >= COLORS) - bg = (t ? t->defbg : default_bg); - - if (!has_default_colors) { - if (fg == -1) - fg = (t && t->deffg != -1 ? t->deffg : default_fg); - if (bg == -1) - bg = (t && t->defbg != -1 ? t->defbg : default_bg); - } - - if (!color2palette || (fg == -1 && bg == -1)) - return 0; - uint index = color_hash(fg, bg); - if (color2palette[index] == 0) { - short oldfg, oldbg; - for (;;) { - if (++color_pair_current >= color_pairs_max) - color_pair_current = color_pairs_reserved + 1; - pair_content(color_pair_current, &oldfg, &oldbg); - uint old_index = color_hash(oldfg, oldbg); - if (color2palette[old_index] >= 0) { - if (init_pair(color_pair_current, fg, bg) == OK) { - color2palette[old_index] = 0; - color2palette[index] = color_pair_current; - } - break; - } - } - } - - short color_pair = color2palette[index]; - return color_pair >= 0 ? color_pair : -color_pair; -} - -short -vt_color_reserve(short fg, short bg) -{ - if (!color2palette || fg >= COLORS || bg >= COLORS) - return 0; - if (!has_default_colors && fg == -1) - fg = default_fg; - if (!has_default_colors && bg == -1) - bg = default_bg; - if (fg == -1 && bg == -1) - return 0; - uint index = color_hash(fg, bg); - if (color2palette[index] >= 0) { - if (init_pair(color_pairs_reserved + 1, fg, bg) == OK) - color2palette[index] = -(++color_pairs_reserved); - } - short color_pair = color2palette[index]; - return color_pair >= 0 ? color_pair : -color_pair; -} - -static -void -init_colors(void) -{ - pair_content(0, &default_fg, &default_bg); - if (default_fg == -1) - default_fg = COLOR_WHITE; - if (default_bg == -1) - default_bg = COLOR_BLACK; - has_default_colors = (use_default_colors() == OK); - color_pairs_max = MIN(MAX_COLOR_PAIRS, SHRT_MAX); - if (COLORS) - color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short)); - /* - * XXX: On undefined color-pairs NetBSD curses pair_content() set fg - * and bg to default colors while ncurses set them respectively to - * 0 and 0. Initialize all color-pairs in order to have consistent - * behaviour despite the implementation used. - */ - for (short i = 1; i < color_pairs_max; i++) - init_pair(i, 0, 0); - vt_color_reserve(COLOR_WHITE, COLOR_BLACK); -} - void -vt_init(void) +vt·init(void) { init_colors(); is_utf8_locale(); char *term = getenv("DVTM_TERM"); if (!term) term = "dvtm"; - snprintf(vt_term, sizeof vt_term, "%s%s", term, COLORS >= 256 ? "-256color" : ""); + + snprintf(vtname, sizeof vtname, "%s%s", term, COLORS >= 256 ? "-256color" : ""); +} + +void +vt·shutdown(void) +{ } +#if 0 void -vt_keytable_set(const char * const keytable_overlay[], int count) +vt·setkeytable(const char * const keytable_overlay[], int count) { for (int k = 0; k < count && k < KEY_MAX; k++) { const char *keyseq = keytable_overlay[k]; @@ -1959,52 +1392,47 @@ vt_keytable_set(const char * const keytable_overlay[], int count) } void -vt_shutdown(void) -{ - free(color2palette); -} - -void -vt_title_handler_set(Vt *t, vt_title_handler_t handler) +vt·settitlecb(Vt *t, vt_title_handler_t handler) { t->title_handler = handler; } void -vt_urgent_handler_set(Vt *t, vt_urgent_handler_t handler) +vt·seturgentcb(Vt *t, vt_urgent_handler_t handler) { t->urgent_handler = handler; } +#endif void -vt_data_set(Vt *t, void *data) +vt·setdata(Vt *t, void *data) { t->data = data; } void * -vt_data_get(Vt *t) +vt·getdata(Vt *t) { return t->data; } bool -vt_cursor_visible(Vt *t) +vt·cursorvisible(Vt *t) { - return t->buffer->scroll_below ? false : !t->curshid; + return t->buffer->scroll.below ? false : !t->curshid; } pid_t -vt_pid_get(Vt *t) +vt·pid(Vt *t) { return t->pid; } size_t -vt_content_get(Vt *t, char **buf, bool colored) +vt·content(Vt *t, char **buf, bool colored) { - Buffer *b = t->buffer; - int lines = b->scroll_above + b->scroll_below + b->rows + 1; + Buffer *b = t->buffer; + int lines = b->scroll.above + b->scroll.below + b->rows + 1; size_t size = lines * ((b->cols + 1) * ((colored ? 64 : 0) + MB_CUR_MAX)); mbstate_t ps; memset(&ps, 0, sizeof(ps)); @@ -2013,28 +1441,28 @@ vt_content_get(Vt *t, char **buf, bool colored) return 0; char *s = *buf; - Cell *prev_cell = nil; + Cell *prev = nil; - for (Row *row = buffer_row_first(b); row; row = buffer_row_next(b, row)) { + for (Row *row = browfirst(b); row; row = brownext(b, row)) { size_t len = 0; char *last_non_space = s; for (int col = 0; col < b->cols; col++) { Cell *cell = row->cells + col; if (colored) { int esclen = 0; - if (!prev_cell || cell->attr != prev_cell->attr) { + if (!prev || cell->attr != prev->attr) { attr_t attr = cell->attr << NCURSES_ATTR_SHIFT; esclen = sprintf(s, "\033[0%s%s%s%s%s%sm", - attr & A_BOLD ? ";1" : "", - attr & A_DIM ? ";2" : "", - attr & A_UNDERLINE ? ";4" : "", - attr & A_BLINK ? ";5" : "", - attr & A_REVERSE ? ";7" : "", - attr & A_INVIS ? ";8" : ""); + attr & PenBold ? ";1" : "", + attr & PenDim ? ";2" : "", + attr & PenUnderline ? ";4" : "", + attr & PenBlink ? ";5" : "", + attr & PenReverse ? ";7" : "", + attr & PenInvis ? ";8" : ""); if (esclen > 0) s += esclen; } - if (!prev_cell || cell->fg != prev_cell->fg || cell->attr != prev_cell->attr) { + if (!prev || cell->fg != prev->fg || cell->attr != prev->attr) { if (cell->fg == -1) esclen = sprintf(s, "\033[39m"); else @@ -2042,7 +1470,7 @@ vt_content_get(Vt *t, char **buf, bool colored) if (esclen > 0) s += esclen; } - if (!prev_cell || cell->bg != prev_cell->bg || cell->attr != prev_cell->attr) { + if (!prev || cell->bg != prev->bg || cell->attr != prev->attr) { if (cell->bg == -1) esclen = sprintf(s, "\033[49m"); else @@ -2050,7 +1478,7 @@ vt_content_get(Vt *t, char **buf, bool colored) if (esclen > 0) s += esclen; } - prev_cell = cell; + prev = cell; } if (cell->text) { len = wcrtomb(s, cell->text, &ps); @@ -2072,7 +1500,7 @@ vt_content_get(Vt *t, char **buf, bool colored) } int -vt_content_start(Vt *t) +vt·contentstart(Vt *t) { - return t->buffer->scroll_above; + return t->buffer->scroll.above; } diff --git a/sys/cmd/dvtm/vt.h b/sys/cmd/dvtm/vt.h index 3b738f6..3d4d129 100644 --- a/sys/cmd/dvtm/vt.h +++ b/sys/cmd/dvtm/vt.h @@ -1,11 +1,13 @@ /* see LICENSE for details */ #pragma once +#include +// #include #include #include #ifndef NCURSES_MOUSE_VERSION -#define mmask_t unsigned long +#define mmask_t ulong #endif typedef struct Vt Vt; @@ -16,11 +18,11 @@ void vt_init(void); void vt_shutdown(void); void vt_keytable_set(char const * const keytable_overlay[], int count); -void vt_default_colors_set(Vt*, attr_t attrs, short fg, short bg); +void vt_default_colors_set(Vt*, attr_t attrs, int fg, int bg); void vt_title_handler_set(Vt*, vt_title_handler_t); void vt_urgent_handler_set(Vt*, vt_urgent_handler_t); void vt_data_set(Vt*, void *); -void *vt_data_get(Vt*); +void *vt_data_get(Vt*); Vt *vt_create(int rows, int cols, int scroll_buf_sz); void vt_resize(Vt*, int rows, int cols); @@ -35,8 +37,8 @@ ssize_t vt_write(Vt*, const char *buf, size_t len); void vt_mouse(Vt*, int x, int y, mmask_t mask); void vt_dirty(Vt*); void vt_draw(Vt*, WINDOW *win, int startrow, int startcol); -short vt_color_get(Vt*, short fg, short bg); -short vt_color_reserve(short fg, short bg); +int vt_color_get(Vt*, int fg, int bg); +int vt_color_reserve(int fg, int bg); void vt_scroll(Vt*, int rows); void vt_noscroll(Vt*); diff --git a/sys/cmd/dvtm/window.c b/sys/cmd/dvtm/window.c new file mode 100644 index 0000000..44690c9 --- /dev/null +++ b/sys/cmd/dvtm/window.c @@ -0,0 +1,42 @@ +#include +#include + +#include "buffer.h" + +typedef struct Rect Rect; +typedef struct Window Window; + +/* origin upper left corner */ +struct Rect +{ + int r0, c0, rows, cols; +}; + +struct Window +{ + Buffer buffer[2], *buf; + Rect area; /* on screen */ + Pen pen, spen; /* current and saved pen */ + struct { + uint visible : 1; + int row, col; /* saved cursor row/colmn (zero based) */ + int srow, scol; /* saved cursor row/colmn (zero based) */ + } c; +} + +/* functions */ + +Window * +makewindow( + + t->pen = (Pen) { + .state = PenNormal, + .col = {-1, -1}, + }; + t->buffer = &t->buf[0]; + + if (!binit(&t->buf[0], rows, cols, size) || + !binit(&t->buf[1], rows, cols, 0)) { + free(t); + return nil; + } diff --git a/sys/cmd/dwm/config.h b/sys/cmd/dwm/config.h index a35e4e6..ce5b196 100644 --- a/sys/cmd/dwm/config.h +++ b/sys/cmd/dwm/config.h @@ -60,12 +60,22 @@ static Layout layouts[] = { static char *menucmd[] = { "menu_run", nil }; static char *termcmd[] = { "term", nil }; static char *webscmd[] = { "qutebrowser", nil }; +static char *upvolcmd[] = { "pactl", "set-sink-volume", "0", "+5%", nil }; +static char *lovolcmd[] = { "pactl", "set-sink-volume", "0", "-5%", nil }; +static char *novolcmd[] = { "pactl", "set-sink-mute", "0", "toggle", nil }; + +#define XK_lovol XF86XK_AudioLowerVolume +#define XK_upvol XF86XK_AudioRaiseVolume +#define XK_novol XF86XK_AudioMute static Key keys[] = { /* modifier key function argument */ { MODKEY, XK_d, spawn, {.v = menucmd } }, { MODKEY, XK_Return, spawn, {.v = termcmd } }, - { MODKEY|ShiftMask, XK_q, spawn, {.v = webscmd } }, + { MODKEY, XK_q, spawn, {.v = webscmd } }, + { 0, XK_upvol, spawn, {.v = upvolcmd} }, + { 0, XK_lovol, spawn, {.v = lovolcmd} }, + { 0, XK_novol, spawn, {.v = novolcmd} }, { MODKEY, XK_b, togglebar, {0} }, { MODKEY, XK_f, togglefocus, {0} }, { MODKEY, XK_j, focusstack, {.i = +1 } }, diff --git a/sys/cmd/dwm/dwm.h b/sys/cmd/dwm/dwm.h index 3449ff8..3f2dd0e 100644 --- a/sys/cmd/dwm/dwm.h +++ b/sys/cmd/dwm/dwm.h @@ -24,6 +24,7 @@ #include #include #include +#include /* macros */ #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) diff --git a/sys/cmd/rules.mk b/sys/cmd/rules.mk index a68231b..2364189 100644 --- a/sys/cmd/rules.mk +++ b/sys/cmd/rules.mk @@ -5,8 +5,8 @@ include share/push.mk DIR := $(d)/cat include $(DIR)/rules.mk -DIR := $(d)/cc -include $(DIR)/rules.mk +# DIR := $(d)/cc +# include $(DIR)/rules.mk # DIR := $(d)/edo # include $(DIR)/rules.mk diff --git a/sys/cmd/term/term.h b/sys/cmd/term/term.h index 00a646a..3c7e3ca 100644 --- a/sys/cmd/term/term.h +++ b/sys/cmd/term/term.h @@ -4,7 +4,6 @@ #include #include -#include #include #include #include -- cgit v1.2.1