/* * vtkbd.c * Virtual Terminal Keyboard * * piaip's new re-implementation of xterm/VT100/220/ANSI key input * escape sequence parser for BBS * * Author: Hung-Te Lin (piaip) * Create: Wed Sep 23 15:06:43 CST 2009 * --------------------------------------------------------------------------- * Copyright (c) 2009 Hung-Te Lin * All rights reserved. * Distributed under BSD license (GPL compatible). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * --------------------------------------------------------------------------- * References: * http://support.dell.com/support/edocs/systems/pe2650/en/ug/5g387ad0.htm * http://aperiodic.net/phil/archives/Geekery/term-function-keys.html * http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=/rzaiw/rzaiwvt220opmode.htm * http://www.rebol.com/docs/core23/rebolcore-18.html * http://ascii-table.com/ansi-escape-sequences-vt-100.php * http://web.mit.edu/gnu/doc/html/screen_10.html * http://vt100.net/docs/vt220-rm/chapter3.html * http://inwap.com/pdp10/ansicode.txt * http://www.connectrf.com/Documents/vt220.html * http://www.ibb.net/~anne/keyboard.html * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * PuTTY Source < terminal.c, term_key() > * Termcap * --------------------------------------------------------------------------- * * BS/DEL Rules * The BackSpace, Erase( [ | O (app) * - Home/Ins/Del/End/PgUp/PgDn: [ <1~6> ~ * - Shift-TAB: [ Z | [ 0 Z * - F1~F4: [ 1 <1234> ~ | O * - F5: [ 1 <5> ~ * - F6-F8: [ 1 <789> ~ * - F9-F12: [ 2 <0134> ~ * - Num 0-9 *+,-./=ENTER: O * * Note: we don't support some rare terms like O described * in Dell 2650 in order to prevent confusion. * Num pad is also always converted to digits. */ #include "bbs.h" /* VtkbdCtx.state */ typedef enum { VKSTATE_NORMAL = 0, VKSTATE_ESC, // VKSTATE_ESC_APP, // O VKSTATE_ESC_QUOTE, // [ VKSTATE_ZERO, // [ 0 (wait Z) VKSTATE_ONE, // [ <1> VKSTATE_TWO, // [ <2> VKSTATE_TLIDE, // [ * (wait ~, return esc_arg) } VKSTATES; /* the processor API */ int vtkbd_process(int c, VtkbdCtx *ctx) { switch (ctx->state) { case VKSTATE_NORMAL: // original state if (c == KEY_ESC) { ctx->state = VKSTATE_ESC; return KEY_INCOMPLETE; } // simple mappings switch (c) { // BS/ERASE/DEL Rules case 0x7F: case 0x08: return KEY_BS; } return c; case VKSTATE_ESC: // switch (c) { case '[': ctx->state = VKSTATE_ESC_QUOTE; return KEY_INCOMPLETE; case 'O': ctx->state = VKSTATE_ESC_APP; return KEY_INCOMPLETE; case '0': ctx->state = VKSTATE_ZERO; return KEY_INCOMPLETE; } // XXX should we map this into another section of KEY_ESC_* ? ctx->esc_arg = c; ctx->state = VKSTATE_NORMAL; return KEY_ESC; case VKSTATE_ZERO: // 0 if (c != 'Z') break; ctx->state = VKSTATE_NORMAL; return KEY_STAB; case VKSTATE_ESC_APP: // O switch (c) { case 'A': case 'B': case 'C': case 'D': ctx->state = VKSTATE_NORMAL; return KEY_UP + (c - 'A'); case 'P': case 'Q': case 'R': case 'S': ctx->state = VKSTATE_NORMAL; return KEY_F1 + (c - 'P'); // Num pads: always convert to NumLock=ON case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': ctx->state = VKSTATE_NORMAL; return '0' + (c - 'p'); case 'M': ctx->state = VKSTATE_NORMAL; return KEY_ENTER; case 'X': ctx->state = VKSTATE_NORMAL; return '='; case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': { static const char *numx = "*+,-./"; assert( c >= 'j' && (c-'j') < strlen(numx)); ctx->state = VKSTATE_NORMAL; return numx[c-'j']; } } break; case VKSTATE_ESC_QUOTE: // [ switch(c) { case 'A': case 'B': case 'C': case 'D': ctx->state = VKSTATE_NORMAL; return KEY_UP + (c - 'A'); case '3': case '4': case '5': case '6': ctx->state = VKSTATE_TLIDE; ctx->esc_arg = KEY_DEL + (c - '3'); return KEY_INCOMPLETE; case 'Z': ctx->state = VKSTATE_NORMAL; return KEY_STAB; case '1': ctx->state = VKSTATE_ONE; return KEY_INCOMPLETE; case '2': ctx->state = VKSTATE_TWO; return KEY_INCOMPLETE; } break; case VKSTATE_ONE: // [ 1 if (c == '~') { ctx->state = VKSTATE_NORMAL; return KEY_HOME; } switch(c) { case '1': case '2': case '3': case '4': case '5': ctx->state = VKSTATE_TLIDE; ctx->esc_arg = KEY_F1 + c - '1'; // F1 .. F5 return KEY_INCOMPLETE; case '7': case '8': case '9': ctx->state = VKSTATE_TLIDE; ctx->esc_arg = KEY_F6 + c - '7'; // F6 .. F8 return KEY_INCOMPLETE; } break; case VKSTATE_TWO: // [ 2 if (c == '~') { ctx->state = VKSTATE_NORMAL; return KEY_INS; // HOME+1 } switch(c) { case '0': case '1': ctx->state = VKSTATE_TLIDE; ctx->esc_arg = KEY_F9 + c - '0'; // F9 .. F10 return KEY_INCOMPLETE; case '3': case '4': ctx->state = VKSTATE_TLIDE; ctx->esc_arg = KEY_F11 + c - '3'; // F11 .. F12 return KEY_INCOMPLETE; } break; case VKSTATE_TLIDE: // Esc [ <12> <0-9> ~ if (c != '~') break; ctx->state = VKSTATE_NORMAL; return ctx->esc_arg; default: assert(!"unknown vkstate"); break; } // what to do now? ctx->state = VKSTATE_NORMAL; return KEY_UNKNOWN; } // vim:sw=4:sw=4:et