aboutsummaryrefslogtreecommitdiff
path: root/sys/libterm
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-06-18 19:45:40 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-06-18 19:45:40 -0700
commit425ef692da7e74112f88f0b368f3286dba84f846 (patch)
treed45729e90010e8d8c539031c3b72165f6884575d /sys/libterm
parent0522b4bf4e125b7ceb67f7177db692aed3a0ebf9 (diff)
feat: working parser for rc shell language
Diffstat (limited to 'sys/libterm')
-rw-r--r--sys/libterm/buffer.c326
-rw-r--r--sys/libterm/escseq.c0
-rw-r--r--sys/libterm/events.c1692
-rw-r--r--sys/libterm/input.c108
-rw-r--r--sys/libterm/term.c256
-rw-r--r--sys/libterm/term.h365
-rw-r--r--sys/libterm/window.c426
7 files changed, 643 insertions, 2530 deletions
diff --git a/sys/libterm/buffer.c b/sys/libterm/buffer.c
deleted file mode 100644
index b903e71..0000000
--- a/sys/libterm/buffer.c
+++ /dev/null
@@ -1,326 +0,0 @@
-#include "term.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->nrow; i++) {
- Row *row = b->row + i;
- for (int j = 0; j < b->ncol; j++) {
- row->cells[j] = cell;
- row->dirty = true;
- }
- }
-}
-
-void
-bfree(Buffer *b)
-{
- int i;
-
- for (i = 0; i < b->nrow; i++)
- free(b->row[i].cells);
-
- free(b->row);
-
- 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 nrow, int ncol)
-{
- Row *row = b->row;
-
- if (b->nrow != nrow) {
- if (b->crow >= row + nrow) {
- /* scroll up instead of simply chopping off bottom */
- bscroll(b, (b->crow - b->row) - nrow + 1);
- }
- while (b->nrow > nrow) {
- free(row[b->nrow - 1].cells);
- b->nrow--;
- }
-
- row = realloc(row, sizeof(Row) * nrow);
- }
-
- if (b->maxcols < ncol) {
- for (int r = 0; r < b->nrow; r++) {
- row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol);
- if (b->ncol < ncol)
- zero(row + r, b->ncol, ncol - b->ncol);
- row[r].dirty = true;
- }
- Row *sbuf = b->scroll.buf;
- for (int r = 0; r < b->scroll.size; r++) {
- sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol);
- if (b->ncol < ncol)
- zero(sbuf + r, b->ncol, ncol - b->ncol);
- }
- b->tabs = realloc(b->tabs, sizeof(*b->tabs) * ncol);
- for (int c = b->ncol; c < ncol; c++)
- b->tabs[c] = !(c & 7);
- b->maxcols = ncol;
- b->ncol = ncol;
- } else if (b->ncol != ncol) {
- for (int r = 0; r < b->nrow; r++)
- row[r].dirty = true;
- b->ncol = ncol;
- }
-
- int deltarows = 0;
- if (b->nrow < nrow) {
- while (b->nrow < nrow) {
- row[b->nrow].cells = calloc(b->maxcols, sizeof(Cell));
- zero(row + b->nrow, 0, b->maxcols);
- b->nrow++;
- }
-
- /* prepare for backfill */
- if (b->crow >= b->scroll.bot - 1) {
- deltarows = b->row + nrow - b->crow - 1;
- if (deltarows > b->scroll.above)
- deltarows = b->scroll.above;
- }
- }
-
- b->crow += row - b->row;
- b->scroll.top = row;
- b->scroll.bot = row + nrow;
- b->row = row;
-
- /* 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->row;
- bboundary(b, &bstart, nil, nil, nil);
- return bstart;
-}
-
-Row *
-browlast(Buffer *b)
-{
- Row *aend;
- if (!b->scroll.size || !b->scroll.below)
- return b->row + b->nrow - 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->row, *last = b->row + b->nrow - 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->row, *last = b->row + b->nrow - 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;
-}
-
-void
-brender(Buffer *b, Term *t)
-{
- int r, c, n;
- char u[UTFmax+1];
- Row *row;
- Cell *cell;
-
- for (r = 0; r < b->nrow; r++) {
- row = b->row + r;
- if (!row->dirty)
- continue;
-
- for (c = 0; c < b->ncol; c++) {
- cell = row->cells + c;
- tsetpen(t, cell->pen);
- n = utf8·runetobyte(u, &cell->r);
- twrite(t, n, u);
- }
- }
-}
diff --git a/sys/libterm/escseq.c b/sys/libterm/escseq.c
deleted file mode 100644
index e69de29..0000000
--- a/sys/libterm/escseq.c
+++ /dev/null
diff --git a/sys/libterm/events.c b/sys/libterm/events.c
deleted file mode 100644
index 80bc99a..0000000
--- a/sys/libterm/events.c
+++ /dev/null
@@ -1,1692 +0,0 @@
-#include "term.h"
-
-#include <poll.h>
-
-#define bufcount(in) in->buf.c - in->buf.b
-
-enum {
- NodeKey,
- NodeArr,
-};
-
-struct Node
-{
- int type;
-};
-
-struct KeyNode
-{
- struct Node;
- struct KeyInfo key;
-};
-
-struct ArrNode
-{
- struct Node;
- uchar min, max;
- Node *arr[];
-};
-
-// -----------------------------------------------------------------------
-// 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) - 1;
-
- if (row)
- *row = ((uchar)key->code.mouse[2] | ((uchar)key->code.mouse[3] & 0x70) << 4) - 1;
-}
-
-// -----------------------------------------------------------------------
-// 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;
- }
-
- // SGR 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]);
-
- 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:
- continue;
- }
- }
- 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
-isreadablekey(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 = isreadablekey(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 = isreadablekey(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 "<unknown>";
-
- 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 ? "<C>" : ""),
- (mode & ModAlt ? "<A>" : ""),
- (mode & ModShift? "<S>" : ""),
- 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("<nil>\n");
- case EvAgain:
- printf("<again>\n");
- default:
- ;
- }
- n++;
- if (n > 200)
- break;
- }
- stoprecord(in);
- freeinput(in);
-}
diff --git a/sys/libterm/input.c b/sys/libterm/input.c
deleted file mode 100644
index 038e8df..0000000
--- a/sys/libterm/input.c
+++ /dev/null
@@ -1,108 +0,0 @@
-#include <u.h>
-#include <libn.h>
-#include <unibilium.h>
-
-#define iota(x) 1 << (x)
-
-// -----------------------------------------------------------------------
-// enums
-
-enum
-{
- KeyRune, KeyFunc, KeySym,
- KeyMouse, KeyPosition, KeyModeReport,
- KeyDCS, KeyOSC, /* add other recognised types here */
- KeyUnknownCSI = -1
-};
-
-enum KeyEvent
-{
- EvNil, EvKey, EvEOF, EvAgain, EvErr,
-};
-
-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, SymKpMul, SymKpDiv, SymKpComma,
- SymKpDot, SymKpEq,
-
- /* et cetera ad nauseum */
- NumSyms
-};
-
-enum MouseEvent
-{
- MouseNil, MousePress, MouseDrag, MouseRelease,
-};
-
-enum
-{
- ModShift = iota(0),
- ModAlt = iota(1),
- ModCtrl = iota(2),
-};
-
-// -----------------------------------------------------------------------
-// types
-
-typedef struct Input Input;
-
-struct Input
-{
- int fd;
- int wait; /* in msec */
- struct {
- char *b, *c, bytes[1024];
- } rbuf; /* read buffer */
- struct {
- char *b, *c, bytes[1024];
- } ebuf; /* escape buffer */
-};
-
-// -----------------------------------------------------------------------
-// globals
-
-// -----------------------------------------------------------------------
-// utility functions
-
-// -----------------------------------------------------------------------
-// implementation
-
-Input *
-makeinput(int fd, int flags, unibi_term *info)
-{
- int i;
- Input *in;
- char *e;
-
- if (!(in = calloc(1, sizeof(in))))
- panicf("out of memory");
-
- in->fd = fd;
- in->wait = 50; /* in msec */
-
- /* initialize buffers */
- in->rbuf.c = in->rbuf.b = in->rbuf.bytes;
-
- return in;
-}
diff --git a/sys/libterm/term.c b/sys/libterm/term.c
index cda2bbe..11591fc 100644
--- a/sys/libterm/term.c
+++ b/sys/libterm/term.c
@@ -1,5 +1,8 @@
#include "term.h"
+#include <signal.h>
+#include <sys/ioctl.h>
+
struct ExtraInfo
{
char *enteralt;
@@ -19,6 +22,8 @@ struct ExtraInfo vt200 =
.exitmouse = "\e[?1002l\e[?1006l",
};
+static Term *sigwinchhead;
+
// -----------------------------------------------------------------------
// database lookup
@@ -78,26 +83,9 @@ guessextrastr(Term *t, char *name, char *guess)
return guess;
}
-static
-int
-tryextrabool(Term *t, char *name)
-{
- const char *nm;
- size_t max = unibi_count_ext_bool(t->info);
- for (size_t i = 0; i < max; i++) {
- nm = unibi_get_ext_bool_name(t->info, i);
- if (nm && !strcmp(nm, name)) {
- return (int)i;
- }
- }
- return -1;
-}
-
/* formats escape strings and writes to output */
static void tfmt(Term *t, char *esc, int n, ...);
-
-// -----------------------------------------------------------------------
-// exported pen methods
+static void tclear(Term *t);
// -----------------------------------------------------------------------
// exported term methods
@@ -113,14 +101,25 @@ ttmpbuf(Term *t, int len)
return (t->tmp.b = realloc(t->tmp.b, len));
}
-void
-tinit(Term *t)
+void twrite(Term *t, long len, char *s);
+void tlistensigwinch(Term *t);
+
+Term*
+tmake(void)
{
+ Term *t;
+
+ t = calloc(1, sizeof(*t));
+
+ /* meta data */
t->name = getenv("TERM");
t->info = unibi_from_term(t->name);
if (!t->info)
panicf("could not identify terminal");
+ t->fd = 1; // stdout
+ tlistensigwinch(t);
+
t->mode.mouse = 0;
t->mode.cursorvis = 1;
t->mode.altscreen = 0;
@@ -128,13 +127,23 @@ tinit(Term *t)
t->cap.colors = unibi_get_num(t->info, unibi_max_colors);
t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase);
- /* TODO */
- t->input = nil;
- t->root = nil;
- t->pen = (Pen){0};
+ /* initialize root window (get current size)*/
+ struct winsize ws = { 0 };
+ if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1)
+ goto bad;
- /* fill in buffers */
- t->buf.c = t->buf.b;
+ t->root = wmake(nil, 0, 0, ws.ws_col, ws.ws_row, 0);
+
+ t->root->curvis = 1;
+ t->root->blink = 0;
+
+ t->pen = (Pen){
+ .state = PenNormal,
+ .col = {.fg = -1, .bg = -1},
+ };
+
+ /* fill in output buffers */
+ t->buf.c = t->buf.b;
t->tmp.b = nil;
t->tmp.len = 0;
@@ -174,22 +183,16 @@ tinit(Term *t)
t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
- /* acs characters */
- t->acs.vline = tryinfostr(t, unibi_acs_vline);
- t->acs.hline = tryinfostr(t, unibi_acs_hline);
- t->acs.plus = tryinfostr(t, unibi_acs_plus);
- t->acs.ltee = tryinfostr(t, unibi_acs_ltee);
- t->acs.rtee = tryinfostr(t, unibi_acs_rtee);
- t->acs.ttee = tryinfostr(t, unibi_acs_ttee);
- t->acs.btee = tryinfostr(t, unibi_acs_btee);
- t->acs.ulcorner = tryinfostr(t, unibi_acs_ulcorner);
- t->acs.urcorner = tryinfostr(t, unibi_acs_urcorner);
- t->acs.llcorner = tryinfostr(t, unibi_acs_llcorner);
- t->acs.lrcorner = tryinfostr(t, unibi_acs_lrcorner);
+ return t;
+
+bad:
+ panicf("failed to initialize terminal instance");
+ free(t);
+ return nil;
}
void
-tfini(Term *t)
+tfree(Term *t)
{
if (t->mode.mouse)
twrite(t, 0, vt200.exitmouse);
@@ -199,6 +202,56 @@ tfini(Term *t)
twrite(t, 0, vt200.exitalt);
tfmt(t, t->esc.sgr0, 0);
+ tclear(t);
+ free(t);
+}
+
+/* handle resize events */
+void
+tresize(Term *t)
+{
+ if (t->fd == -1)
+ return;
+
+ struct winsize ws = { 0 };
+ if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1)
+ return;
+
+ printf("[%d,%d]\n", ws.ws_col, ws.ws_row);
+ if (t->root->w != ws.ws_col || t->root->h != ws.ws_row)
+ wresize(t->root, ws.ws_col, ws.ws_row);
+}
+
+static
+void
+sigwinch(int num)
+{
+ Term *it;
+ for (it = sigwinchhead; it; it = it->link)
+ tresize(it);
+}
+
+void
+tlistensigwinch(Term *t)
+{
+ sigset_t new, old;
+ Term *it;
+
+ sigemptyset(&new);
+ sigaddset(&new, SIGWINCH);
+ sigprocmask(SIG_BLOCK, &new, &old);
+
+ if (!sigwinchhead) {
+ sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = sigwinch }, nil);
+ sigwinchhead = t;
+ } else {
+ it = sigwinchhead;
+ while (it->link)
+ it = it->link;
+ it->link = t;
+ }
+
+ sigprocmask(SIG_SETMASK, &old, nil);
}
void
@@ -217,12 +270,14 @@ twrite(Term *t, long len, char *s)
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;
+loop:
+ n = MIN(len, arrend(t->buf.b) - t->buf.c);
+ memcpy(t->buf.c, s, n);
+ t->buf.c += n;
+ len -= n;
+ if (len) {
tflush(t);
+ goto loop;
}
}
@@ -256,8 +311,8 @@ tsetpen(Term *t, Pen new)
/* fg/bg color */
/* TODO: add a check for if the terminal supports true color */
- /* TODO: add a check for negative indices */
- if (new.state & PenTrueClr) {
+ /* TODO: deal w/ negative indices properly */
+ if (new.state & PenRGB) {
tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b);
tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b);
} else {
@@ -282,12 +337,13 @@ tfmt(Term *t, char *esc, int n, ...)
panicf("no terminfo escape string given");
va_start(args, n);
- for (i = 0; i < arrlen(param) && i < n; i++)
+ 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)) {
+ if (len >= arrlen(buf)) {
c = ttmpbuf(t, len);
unibi_run(esc, param, c, len);
}
@@ -296,6 +352,7 @@ tfmt(Term *t, char *esc, int n, ...)
}
/* absolute move */
+static
int
tgoto(Term *t, int row, int col)
{
@@ -324,6 +381,7 @@ tgoto(Term *t, int row, int col)
}
/* relative move */
+static
void
tjump(Term *t, int down, int right)
{
@@ -346,50 +404,86 @@ tjump(Term *t, int down, int right)
tfmt(t, t->esc.cub, 1, -right);
}
+static
void
-tdel(Term *t, int num)
+tclear(Term *t)
{
- 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);
- }
+ tfmt(t, t->esc.ed2, 0);
}
void
-tclear(Term *t)
+tblit(Term *t, Window *win)
{
- tfmt(t, t->esc.ed2, 0);
+ int r, c, n, j;
+ Row *row;
+ char u[UTFmax+1] = {0};
+
+ j = 0;
+ tgoto(t, win->top, win->left);
+ for (r = 0; r < win->h; r++) {
+ row = win->row + r;
+ if (!row->dirty) {
+ j++;
+ continue;
+ }
+
+ if (j) {
+ tjump(t, j, 0);
+ j = 0;
+ }
+
+ for (c = 0; c < win->w; c++) {
+ tsetpen(t, row->cells[c].pen);
+ n = utf8·runetobyte(u, &row->cells[c].txt);
+ twrite(t, n, u);
+ }
+
+ row->dirty = 0;
+ }
+
+ tflush(t);
}
// -----------------------------------------------------------------------
-// testing entry
+// testing
int
main()
{
- Term t;
- tinit(&t);
-
- printf("%s: %s\n", unibi_short_name_str(unibi_set_a_foreground), t.esc.sgr_fg);
- printf("%s: %s\n", unibi_short_name_str(unibi_set_a_background), t.esc.sgr_bg);
- tfmt(&t, t.esc.ext.rgbf, 3, 10, 10, 100);
- tfmt(&t, t.esc.ext.rgbb, 3, 100, 0, 100);
- twrite(&t, 0, "hello world\n");
- twrite(&t, 0, "hello world\n");
- twrite(&t, 0, t.acs.hline);
-
- tfini(&t);
+ int i;
+ Term *t;
+ Window *win;
+
+ t = tmake();
+ win = t->root;
+ tclear(t);
+
+ win->pen = (Pen){
+ .state = PenNormal,
+ .col = {.fg=-1, .bg=-1},
+ };
+ for (i = 0; i < 2000; i++)
+ wputrune(win, 'a');
+
+ tblit(t, win);
+
+ win->cur.row = 10;
+ win->cur.col = 0;
+
+ win->pen = (Pen){
+ .state=PenNormal|PenRGB,
+ .rgb={.fg={200, 100, 100}, .bg={0, 0, 0} },
+ };
+
+ for (i = 0; i < 500; i++)
+ wputrune(win, 'b');
+
+ tblit(t, win);
+
+ sleep(5);
+ wscroll(win, 10);
+ tblit(t, win);
+ sleep(5);
+
+ tfree(t);
}
diff --git a/sys/libterm/term.h b/sys/libterm/term.h
index acae95f..6bd2f6b 100644
--- a/sys/libterm/term.h
+++ b/sys/libterm/term.h
@@ -7,165 +7,11 @@
#include <unibilium.h>
#define iota(x) 1 << (x)
-/*
- * obtained from:
- * https://invisible-island.net/ncurses/man/curs_add_wch.3x.html
- */
-#define ACSRUNES \
- /* name utf8 ascii acsc*/ \
- ACSRUNE("block", 0x25ae, '#', '0') \
- ACSRUNE("board", 0x2592, '#', 'h') \
- ACSRUNE("btee", 0x2534, '+', 'v') \
- ACSRUNE("bullet", 0x00b7, 'o', '~') \
- ACSRUNE("ckboard", 0x2592, ':', 'a') \
- ACSRUNE("darrow", 0x2193, 'v', '.') \
- ACSRUNE("degree", 0x00b0, '\'','f') \
- ACSRUNE("diamond", 0x25c6, '+', '`') \
- ACSRUNE("gequal", 0x2265, '>', '>') \
- ACSRUNE("hline", 0x2500, '-', 'q') \
- ACSRUNE("antern", 0x2603, '#', 'i') \
- ACSRUNE("larrow", 0x2190, '<', ',') \
- ACSRUNE("lequal", 0x2264, '<', 'y') \
- ACSRUNE("llcorner", 0x2514, '+', 'm') \
- ACSRUNE("lrcorner", 0x2518, '+', 'j') \
- ACSRUNE("ltee", 0x2524, '+', 't') \
- ACSRUNE("nequal", 0x2260, '!', '|') \
- ACSRUNE("pi", 0x03c0, '*', '{') \
- ACSRUNE("plminus", 0x00b1, '#', 'g') \
- ACSRUNE("plus", 0x253c, '+', 'n') \
- ACSRUNE("rarrow", 0x2192, '>', '+') \
- ACSRUNE("rtee", 0x251c, '+', 'u') \
- ACSRUNE("s1", 0x23ba, '-', 'o') \
- ACSRUNE("s3", 0x23bb, '-', 'p') \
- ACSRUNE("s7", 0x23bc, '-', 'r') \
- ACSRUNE("s9", 0x23bd, '_', 's') \
- ACSRUNE("sterling", 0x00a3, 'f', '}') \
- ACSRUNE("ttee", 0x252c, '+', 'w') \
- ACSRUNE("uarrow", 0x2191, '^', '-') \
- ACSRUNE("ulcorner", 0x250c, '+', 'l') \
- ACSRUNE("urcorner", 0x2510, '+', 'k') \
- ACSRUNE("vline", 0x2502, '|', 'x') \
- /* thick versions */ \
- ACSRUNE("t_btee", 0x253b, '+', 'V') \
- ACSRUNE("t_hline", 0x2501, '-', 'Q') \
- ACSRUNE("t_llcorner", 0x2517, '+', 'M') \
- ACSRUNE("t_lrcorner", 0x251b, '+', 'J') \
- ACSRUNE("t_ltee", 0x252b, '+', 'T') \
- ACSRUNE("t_plus", 0x254b, '+', 'N') \
- ACSRUNE("t_rtee", 0x2523, '+', 'U') \
- ACSRUNE("t_ttee", 0x2533, '+', 'W') \
- ACSRUNE("t_ulcorner", 0x250f, '+', 'L') \
- ACSRUNE("t_urcorner", 0x2513, '+', 'K') \
- ACSRUNE("t_vline", 0x2503, '|', 'X') \
- /* double version */ \
- ACSRUNE("d_btee", 0x2569, '+', 'H') \
- ACSRUNE("d_hline", 0x2550, '-', 'R') \
- ACSRUNE("d_llcorner", 0x255a, '+', 'D') \
- ACSRUNE("d_lrcorner", 0x255d, '+', 'A') \
- ACSRUNE("d_ltee", 0x2560, '+', 'F') \
- ACSRUNE("d_plus", 0x256c, '+', 'E') \
- ACSRUNE("d_rtee", 0x2563, '+', 'G') \
- ACSRUNE("d_ttee", 0x2566, '+', 'I') \
- ACSRUNE("d_ulcorner", 0x2554, '+', 'C') \
- ACSRUNE("d_urcorner", 0x2557, '+', 'B') \
- ACSRUNE("d_vline", 0x2551, '|', 'Y')
-
-/* enums */
-
-/* key symbols */
-enum
-{
- SymUnknown = -1,
- SymNone = 0,
-
- /* special names in c0 */
- SymBackspace, SymTab, SymEnter, SymEscape,
-
- /* special names in g0 */
- SymSpace, SymDel,
-
- /* special keys */
- SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert,
- SymDelete, SymSelect, SymPageup, SymPagedown, SymHome, SymEnd,
-
- /* special keys from terminfo */
- SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit,
- SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions,
- SymPrint, SymRedo, SymReference, SymRefresh, SymReplace,
- SymRestart, SymResume, SymSave, SymSuspend, SymUndo,
-
- /* numeric keypad special keys */
- SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8,
- SymKp9, SymKpenter, SymKpplus, SymKpminus, SymKpmult, SymKpdiv, SymKpcomma,
- SymKpperiod, SymKpequals,
-
- /* et cetera ad nauseum */
- NumSyms
-};
-
-/* key type */
-enum
-{
- KeyUnicode,
- KeyFunc,
- KeySym,
- KeyMouse,
- KeyPosition,
- KeyModeReport,
- KeyDCS,
- KeyOSC,
- /* add other recognised types here */
-
- KeyUnknownCSI = -1
-};
-
-/* key events */
-enum KeyEvent
-{
- EvNil,
- EvKey,
- EvEOF,
- EvAgain,
- EvErr,
-};
-
-enum MouseEvent
-{
- MouseNil,
- MousePress,
- MouseDrag,
- MouseRelease,
-};
-
-enum
-{
- ModShift = iota(0),
- ModAlt = iota(1),
- ModCtrl = iota(2),
-};
-
-enum
-{
- FlagNoInterpret = iota(0),
- FlagConvertKP = iota(1),
- FlagRaw = iota(2),
- FlagUTF8 = iota(3),
- FlagNoTermIOS = iota(4),
- FlagSpaceSymbol = iota(5),
- FlagCtrlC = iota(6),
- FlagEintr = iota(7),
-};
-
-enum
-{
- HarmonizeSpace = iota(0),
- HarmonizeDelBS = iota(1),
-};
-/* types */
typedef struct RGB8 RGB8;
typedef struct Pen Pen;
+typedef struct Dot Dot;
typedef struct Cell Cell;
typedef struct Row Row;
typedef struct Buffer Buffer;
@@ -194,7 +40,7 @@ enum
PenUnderline = iota(6),
PenBlink = iota(7),
/* ... */
- PenTrueClr = iota(15),
+ PenRGB = iota(15),
};
struct Pen
@@ -213,38 +59,29 @@ struct Pen
};
/* outputs */
-struct Cell {
- rune r;
+struct Cell
+{
+ rune txt;
Pen pen;
};
-struct Row {
+struct Row
+{
Cell *cells;
- uint dirty:1;
+ 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.
+struct Dot
+{
+ int row, col;
+};
+
+/*
+ * scroll.top & scroll.bot are pointers into the viewport.
*
* scroll back buffer
*
- * scroll_buf->+----------------+-----+
+ * scroll.buf->+----------------+-----+
* | | | ^ \
* | before | | | |
* current terminal content | viewport | | | |
@@ -253,39 +90,48 @@ struct Row {
* ^ | | 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
+ * | | i | \ | | i | l /
+ * | buffer | s | >|<- scroll.index | s | l \
+ * h | | i | / | | i | |
+ * | | b | / | after | b | s > scroll.below
* | | l | / | viewport | l | i |
* v | | e | / | | e | z /
* +----------------+-----+/ | unused | | e
- * <- maxcols -> | scroll back | |
- * <- cols -> | buffer | | |
+ * <- maxw -> | scroll back | |
+ * <- w -> | buffer | | |
* | | | |
* | | | v
- * roll_buf + scroll.size->+----------------+-----+
- * <- maxcols ->
- * <- cols ->
+ * scroll.buf + scroll.size->+----------------+-----+
+ * <- maxw ->
+ * <- w ->
*/
-struct Buffer {
- Row *row; /* array of Row pointers of size 'rows' */
- Row *crow; /* row on which the cursor currently resides */
- bool *tabs; /* a boolean flag for each column whether it is a tab */
+struct Buffer
+{
+ int w, h; /* dimension of buffer */
+ Pen pen; /* default attributes */
+ int maxw; /* allocated cells (maximal cols over time) */
+ Row *row; /* array of row pointers of size 'h' */
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 */
+ Row *buf;
+ Row *top;
+ Row *bot;
+ int size;
+ int index;
+ int above;
+ int below;
} scroll;
- Pen pen, spen;
- int nrow, ncol; /* current dimension of buffer */
- int maxcols; /* allocated cells (maximal cols over time) */
- int srow, scol; /* saved cursor row/column (zero based) */
+ Dot cur, save; /* cursor position within buffer */
+};
+
+struct Window
+{
+ struct Buffer;
+ int top, left;
+ uchar curvis : 1;
+ uchar blink : 2;
+
+ Window *parent, *child, *link;
};
/* input */
@@ -310,61 +156,57 @@ struct KeyInfo
int modset;
};
-/* make opaque? */
struct Input
{
int fd;
int flags;
- int hflag;
int wait; /* in ms */
+ /* modifiers */
+ uchar closed : 1;
+ uchar started : 1;
+ uchar hasold : 1;
+
struct termios oldterm;
/* buffer */
struct {
long off;
uchar *b, *c, *e, bytes[256];
- } buf;
-
- /* modifiers */
- char closed : 1;
- char started : 1;
- char hasold : 1;
+ } rbuf;
+ struct {
+ uchar *s, bytes[256];
+ } ebuf;
/* key data */
Node *keys;
struct KeyInfo c0[32];
-
- char **keynm;
- int nkeynm;
-
- int nsavedcsi;
- char *savedcsi;
};
+
struct Term
{
/* meta data */
char *name;
unibi_term *info;
struct {
- uint altscreen : 1;
- uint cursorvis : 1;
- uint mouse : 1;
+ uchar altscreen : 1;
+ uchar cursorvis : 1;
+ uchar mouse : 1;
} mode;
struct {
- uint bce : 1;
- int colors;
+ uchar bce : 1;
+ int colors;
} cap;
/* input capture */
- Input *input;
+ Input input;
/* output display */
Window *root;
Pen pen;
- /* output raw text */
+ /* raw text to pty */
int fd;
struct {
char *c, b[512];
@@ -416,74 +258,13 @@ struct Term
} ext;
} esc;
- /* basic shapes */
- struct {
- rune block;
- rune board;
- rune hline;
- rune vline;
- rune plus;
- rune ltee;
- rune rtee;
- rune ttee;
- rune btee;
- rune ulcorner;
- rune urcorner;
- rune llcorner;
- rune lrcorner;
- } acs;
+ Term *link;
};
-/*
- * exported functions
- */
+/* functions */
+void tresize(Term *t);
-#if 0
-/* buffer.c */
-void zero(Row *row, int start, int len);
-void roll(Row *start, Row *end, int count);
-
-void bclear(Buffer *b);
-void bfree(Buffer *b);
-void bscroll(Buffer *b, int s);
-void bresize(Buffer *b, int rows, int cols);
-bool binit(Buffer *b, int rows, int cols, int size);
-void brender(Buffer *b, Term *t);
-void bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) ;
-Row *browfirst(Buffer *b);
-Row *browlast(Buffer *b);
-Row *brownext(Buffer *b, Row *row);
-Row *bprevrow(Buffer *b, Row *row);
-
-/* input.c */
-Input *makeinput(int fd, int flags, unibi_term *info);
-void freeinput(Input *in);
-int startrecord(Input *in);
-int stoprecord(Input *in);
-char *keyname(Input *in, int sym);
-
-enum KeyEvent term·waitkey(Input *in, Key *key); /* block until next keypress */
-enum KeyEvent term·getkey(Input *in, Key *key); /* grab key if we can */
-enum KeyEvent term·demandkey(Input *in, Key *key); /* grab now and interpret as best we can */
-
-/* unpack key event into useful data */
-enum KeyEvent decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col);
-enum KeyEvent decodepos(Input *in, Key *key, int *row, int *col);
-enum KeyEvent decodemode(Input *in, Key *key, int *init, int *mode, int *val);
-#endif
-
-/* term.c */
-void term·init(Term *t);
-void term·fini(Term *t);
-void term·flush(Term *t);
-void term·write(Term *t, long len, char *s);
-int term·goto(Term *t, int row, int col);
-void term·jump(Term *t, int down, int right);
-void term·del(Term *t, int num);
-void term·setpen(Term *t, Pen pen);
-void term·clear(Term *t);
-
-/* input.c */
-enum KeyEvent term·waitkey(Term *t, Key *key); /* block until next keypress */
-enum KeyEvent term·getkey(Term *t, Key *key); /* grab key if we can */
-enum KeyEvent term·demandkey(Term *t, Key *key); /* grab now and interpret as best we can */
+Window *wmake(Window *root, int top, int left, int w, int h, int scroll);
+void wresize(Window *root, int w, int h);
+void wputrune(Window *win, rune r);
+void wscroll(Window *win, int s);
diff --git a/sys/libterm/window.c b/sys/libterm/window.c
index fec3997..5d36c8b 100644
--- a/sys/libterm/window.c
+++ b/sys/libterm/window.c
@@ -1,44 +1,408 @@
-#include <u.h>
-#include <libn.h>
-
#include "term.h"
-typedef struct Rect Rect;
+// -----------------------------------------------------------------------
+// buffers
-struct Rect
+static
+void
+zero(Row *row, int start, int len)
{
- int top, left, rows, cols;
-};
+ int i;
+ Cell cell = {
+ .txt = L' ',
+ .pen = {
+ .state = PenNormal,
+ .col.fg = -1,
+ .col.bg = -1,
+ },
+ };
+
+ for (i = start; i < len + start; i++)
+ row->cells[i] = cell;
+ row->dirty = 1;
+}
-struct Window
+static
+void
+roll(Row *start, Row *end, int count)
{
- Buffer buffer[2], *buf;
- Rect area; /* on screen */
- Pen pen, spen; /* current and saved pen */
- uint curvis : 1;
- uint damage : 1;
-};
+ int n = end - start;
-/* functions */
+ /* enforce circularity */
+ count %= n;
+ if (count < 0)
+ count += n;
-Window *
-makewindow(Window *root, Rect area, int history)
+ if (count) {
+ char buf[count * sizeof(Row)]; /* XXX: remove VLA */
+ 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 = 1;
+ }
+}
+
+/* buffer operations */
+static
+void
+bclear(Buffer *b)
{
- Window *w;
- w = calloc(1, sizeof(*w));
- if (!w)
- panicf("out of memory");
+ int i;
+ Cell cell = {
+ .txt = L' ',
+ .pen = {
+ .state = PenNormal,
+ .col.fg = -1,
+ .col.bg = -1,
+ },
+ };
+
+ for (i = 0; i < b->h; i++) {
+ Row *row = b->row + i;
+ for (int j = 0; j < b->w; j++) {
+ row->cells[j] = cell;
+ row->dirty = 1;
+ }
+ }
+}
- w->pen = (Pen) {
- .state = PenNormal,
- .col = {-1, -1},
- };
+static
+void
+bfini(Buffer *b)
+{
+ int i;
- if (!binit(w->buffer+0, area.rows, area.cols, history) ||
- !binit(w->buffer+1, area.rows, area.cols, 0)) {
- free(w);
- return nil;
+ for (i = 0; i < b->h; i++)
+ free(b->row[i].cells);
+
+ free(b->row);
+
+ if (b->scroll.size) {
+ for (i = 0; i < b->scroll.size; i++)
+ free(b->scroll.buf[i].cells);
+
+ free(b->scroll.buf);
+ }
+}
+
+static
+void
+bscroll(Buffer *b, int s)
+{
+ Row tmp;
+ int i, ssz = b->scroll.bot - b->scroll.top;
+
+ /* work in quanta of screen size */
+ 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;
+ b->scroll.above = CLAMP(b->scroll.above, 0, b->scroll.size);
+
+ if (s > 0) {
+ if (b->scroll.size) {
+ for (i = 0; i < s; i++) {
+ 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;
+ }
+ } else
+ for (i = 0; i < s; i++)
+ zero(b->scroll.top+i, 0, b->maxw);
+ }
+
+ roll(b->scroll.top, b->scroll.bot, s);
+
+ if (s < 0) {
+ if (b->scroll.size) {
+ for (i = (-s) - 1; i >= 0; i--) {
+ b->scroll.index--;
+ if (b->scroll.index == -1)
+ b->scroll.index = b->scroll.size - 1;
+
+ 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 = 1;
+ }
+ } else
+ for (i = (-s) - 1; i >= 0; i--)
+ zero(b->scroll.top+i, 0, b->maxw);
+ }
+}
+
+static
+void
+bresize(Buffer *b, int nrow, int ncol)
+{
+ int r, d;
+ Row *row = b->row;
+ Row *cur = row + b->cur.row;
+
+ if (b->h != nrow) {
+ /* scroll if we can */
+ if (cur >= row + nrow)
+ bscroll(b, b->cur.row - nrow + 1);
+ while (b->h > nrow) {
+ free(row[b->h - 1].cells);
+ b->h--;
+ }
+
+ row = realloc(row, sizeof(Row) * nrow);
+ }
+
+ if (b->maxw < ncol) {
+ /* expand each row */
+ for (r = 0; r < b->h; r++) {
+ row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol);
+ if (b->h < ncol)
+ zero(row + r, b->w, ncol - b->w);
+ row[r].dirty = 1;
+ }
+ /* expand the scroll buffer */
+ Row *sbuf = b->scroll.buf;
+ for (r = 0; r < b->scroll.size; r++) {
+ sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol);
+ if (b->w < ncol)
+ zero(sbuf + r, b->w, ncol - b->w);
+ }
+ b->maxw = b->w = ncol;
+ } else if (b->w != ncol) {
+ for (r = 0; r < b->h; r++)
+ row[r].dirty = 1;
+ b->w = ncol;
+ }
+
+ d = 0;
+ if (b->h < nrow) {
+ while (b->h < nrow) {
+ row[b->h].cells = calloc(b->maxw, sizeof(Cell));
+ zero(row + b->h, 0, b->maxw);
+ b->h++;
+ }
+
+ /* prepare for backfill */
+ if (cur >= b->scroll.bot - 1) {
+ d = b->row + nrow - cur - 1;
+ if (d > b->scroll.above)
+ d = b->scroll.above;
+ }
}
- w->buf = w->buffer;
- return w;
+
+ b->cur.row += row - b->row;
+ b->scroll.top = row;
+ b->scroll.bot = row + nrow;
+ b->row = row;
+
+ /* perform backfill */
+ if (d > 0) {
+ bscroll(b, -d);
+ b->cur.row += d;
+ }
+}
+
+static
+bool
+binit(Buffer *b, int cols, int rows, int scroll)
+{
+ int size;
+
+ b->pen.state = PenNormal;
+ b->pen.col.fg = b->pen.col.bg = -1;
+
+ size = MAX(scroll, 0);
+ if (size && !(b->scroll.buf = calloc(size, sizeof(Row))))
+ return false;
+
+ b->scroll.size = size;
+ bresize(b, rows, cols);
+
+ b->cur = (Dot){0};
+ b->save = b->cur;
+
+ return true;
+}
+
+static
+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];
+ }
+}
+
+static
+Row *
+browfirst(Buffer *b)
+{
+ Row *bstart;
+ if (!b->scroll.size || !b->scroll.above)
+ return b->row;
+ bboundary(b, &bstart, nil, nil, nil);
+ return bstart;
+}
+
+static
+Row *
+browlast(Buffer *b)
+{
+ Row *aend;
+ if (!b->scroll.size || !b->scroll.below)
+ return b->row + b->h - 1;
+ bboundary(b, nil, nil, nil, &aend);
+ return aend;
+}
+
+static
+Row *
+brownext(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->row, *last = b->row + b->h - 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;
+}
+
+static
+Row *
+bprevrow(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->row, *last = b->row + b->h - 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;
+}
+
+// -----------------------------------------------------------------------
+// windows
+
+Window *
+wmake(Window *root, int top, int left, int w, int h, int scroll)
+{
+ Window *child, *it;
+
+ child = calloc(1, sizeof(*child));
+ child->top = top;
+ child->left = left;
+ child->parent = root;
+ if (root) {
+ if (root->child) {
+ for (it = root->child; it->link != nil; it = it->link)
+ ;
+ it->link = child;
+ } else
+ root->child = child;
+
+ child->curvis = root->curvis;
+ child->blink = root->blink;
+ }
+
+ if (!binit((Buffer*)child, w, h, scroll)) {
+ free(child);
+ return nil;
+ }
+
+ return child;
+}
+
+void
+wfree(Window *win)
+{
+ free(win);
+}
+
+void
+wresize(Window *win, int w, int h)
+{
+ bresize((Buffer*)win, w, h);
+}
+
+/* TODO: more sophisticated damage tracking */
+void
+wputrune(Window *win, rune r)
+{
+ Row *row = win->row + win->cur.row;
+ Cell *cell = row->cells + win->cur.col;
+
+ cell->pen = win->pen;
+ cell->txt = r;
+
+ if (win->cur.col++ >= win->w) {
+ win->cur.col = 0;
+ if (win->cur.row++ >= win->h)
+ win->cur.row = win->h-1;
+ }
+ row->dirty = 1;
+}
+
+void
+wscroll(Window *win, int s)
+{
+ bscroll((Buffer*)win, s);
}