#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); }