Parsing ANSI/VT Terminal Keycode Sequences ========================================== Parsing input from an ANSI keyboard stream can be optimised by combining all keycode sequences into a superset that represents all sources: {'?' or 'O' or '['} {{;}} {'@'-'~'} This can be parsed with the following 'C' code. It returns a character code < 0x100 for normal character keys, 0x180+n for function keys, and 0x1C0+n for editing keys. With function and editing keys, the modifier keys are returned in bit 4 and bit 5. unsigned char ansikey[]={ /* Translation table */ 0x00,0xC8,0xC6,0xC7,0xC9,0xCB,0xCA,0xC8, /* 0,Home,Ins,Del,End,PgUp,PgDn,Home */ 0xC9,0x00,0x80,0x81,0x82,0x83,0x84,0x85, /* End,(9),F0,F1,F2,F3,F4,F5 */ 0x00,0x86,0x87,0x88,0x89,0x8A,0x00,0x8B, /* (16),F6,F7,F8,F9,F10,(22),F11 */ 0x8C,0x8D,0x8E,0x00,0x8F,0x00,0x00,0x00, /* F12,F13,F14,(27),F15,F16,(30),F17 */ 0x80,0x00,0xC3,0x00, /* F18,F19,F20,(35) */ 0x84,0xCF,0xCE,0xCD,0xCC,0xC5,0xC9,0x00, /* f4,Up,Dwn,Rgt,Lft,Bgn,End,Nxt */ 0xC8,0x09,0x00,0x95,0x94,0x0D,0x00,0x00, /* Hme,Tab,Clr,Shf5,Shf4,Kent,(N),(O)*/ 0x81,0x82,0x83,0x84,0x85,0xCA,0xCB,0x00, /* f1,f2,f3,f4,f5,PgDn,PgUp,(W) */ 0x3D,0x00,0xD5,0x00,0x00,0x00,0x00,0x00 /* K=,(Y),ShTAB,([),(\),(]),(^),(_) */ } int ch=0; /* Character pressed */ int key=0; /* Key lookup */ int mod=0; /* Modifier= */ fflush(stdout); read(STDIN_FILENO, &ch, 1); /* Read without flushing */ ch=ch & 0xFF; if (ch != 27) return ch; /* Not */ if (kbhit() == 0) return ch; /* Nothing pending */ /* Read an ANSI/VT key sequence as: * [ () (;) <0x40-0x7F> * or O <0x40-0x7F> * or ? <0x40-0x7F> * or <0x40-0x7F> */ ch=getchar(); if (ch=='O' || ch='?') { /* Convert O or ? to [1 */ ch=getchar(); /* Get ? or O */ } else { if (ch == '[') { /* Parse [( */ while ((ch=getchar())<'@') { /* Parse through non-alphas */ if (ch>='0' && ch<='9') { /* Digit, add to current num */ mod=mod*10+(ch-'0'); } if (ch==';') { /* Semicolon, next number */ key=mod; mod=0; } } if (mod) mod=mod-1; /* Convert modifiers to bitmap */ } } if (ch<'@' || ch>0x7F) return ch; /* Not ..., return */ /* mod=0, key=0, ch= ? mod=0, key=0, ch= O mod=0, key=0, ch= [ mod=0, key=0, ch= [ mod=, key=0, ch= [; mod=, key=, ch= */ if (key == 0) { if (ch >= '`') return (ch & 0x3F) | 0x100; /* ? or O */ else key=36+(ch & 0x1F); /* cursors/etc -> key 36+ */ } ch=ansikey[key]; /* Translate keypress */ if (mod & 1) ch=ch ^ 0x10; /* SHIFT pressed */ if (mod & 4) ch=ch ^ 0x20; /* CTRL pressed */ if (mod & 2) ch=ch ^ 0x30; /* ALT pressed */ return (ch | 0x100); Example code: * JGH Console Library - mdfs.net/System/C/Lib * PDP11 ANSI parsing - mdfs.net/Info/Comp/PDP11/ProgTips * PDP11 BBC BASIC Console I/O - mdfs.net/Software/PDP11/BBCBASIC References ---------- * invisible-island.net/xterm/ctlseqs/ctlseqs.html * www.gnu.org/software/screen/manual/html_node/Input-Translation.html#Input-Translation Updates ------- 22-Jun-2020: Testing suggests malformed sequences are ignored. Slight formatting changes.