From 425ef692da7e74112f88f0b368f3286dba84f846 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Thu, 18 Jun 2020 19:45:40 -0700 Subject: feat: working parser for rc shell language --- sys/libterm/events.c | 1692 -------------------------------------------------- 1 file changed, 1692 deletions(-) delete mode 100644 sys/libterm/events.c (limited to 'sys/libterm/events.c') 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 - -#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 ""; - - 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); -} -- cgit v1.2.1