diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2009-09-28 23:06:16 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2009-09-28 23:06:16 +0800 |
commit | c0dd6835cc9f376ad6f195bad2daacff12b5efd9 (patch) | |
tree | 424ea7c2335aa863f8fdc634a0f6b17da8b2641a /mbbsd | |
parent | 4621028ffd6a8acbc2bc76c528f11315812c7049 (diff) | |
download | pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.tar pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.tar.gz pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.tar.bz2 pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.tar.lz pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.tar.xz pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.tar.zst pttbbs-c0dd6835cc9f376ad6f195bad2daacff12b5efd9.zip |
* new keyboard input framework: vtkbd
* BS/BS2 is now merged to BS.
* DEL/Ctrl-D is not mreged.
* pager code (waterball) is a little refined but still far from good.
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@4887 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd')
-rw-r--r-- | mbbsd/Makefile | 2 | ||||
-rw-r--r-- | mbbsd/edit.c | 3 | ||||
-rw-r--r-- | mbbsd/io.c | 472 | ||||
-rw-r--r-- | mbbsd/name.c | 4 | ||||
-rw-r--r-- | mbbsd/register.c | 5 | ||||
-rw-r--r-- | mbbsd/talk.c | 2 | ||||
-rw-r--r-- | mbbsd/visio.c | 2 | ||||
-rw-r--r-- | mbbsd/vtkbd.c | 279 |
8 files changed, 483 insertions, 286 deletions
diff --git a/mbbsd/Makefile b/mbbsd/Makefile index 6b15e1dc..98335987 100644 --- a/mbbsd/Makefile +++ b/mbbsd/Makefile @@ -10,7 +10,7 @@ SRCROOT= .. PROG= mbbsd COREOBJS = bbs.o announce.o read.o board.o cache.o cal.o brc.o mail.o record.o fav.o ACCOBJS = user.o register.o passwd.o emaildb.o -NETOBJS = mbbsd.o io.o term.o telnet.o +NETOBJS = mbbsd.o io.o term.o telnet.o vtkbd.o TALKOBJS = talk.o chat.o friend.o UTILOBJS = stuff.o kaede.o convert.o name.o syspost.o PAGEROBJS= more.o pmore.o diff --git a/mbbsd/edit.c b/mbbsd/edit.c index f39280d4..676fc7de 100644 --- a/mbbsd/edit.c +++ b/mbbsd/edit.c @@ -3931,8 +3931,7 @@ vedit2(const char *fpath, int saveheader, int *islocal, char title[STRLEN], int case KEY_INS: /* Toggle insert/overwrite */ curr_buf->insert_mode ^= 1; break; - case KEY_BS: - case KEY_BS2: /* backspace */ + case KEY_BS: /* backspace */ block_cancel(); if (curr_buf->ansimode) { curr_buf->ansimode = 0; @@ -279,15 +279,7 @@ dogetch(void) lastact = now; } - // CRLF Handle: - // - // (UNIX) LF - // (WIN) CRLF - // (MAC) CR - // - // to work in a compatible way, (see KEY_ENTER definition) - // let KEY_ENTER = CR - + // see vtkbd.c for CR/LF Rules { unsigned char c = (unsigned char) inbuf[icurrchar++]; @@ -301,24 +293,9 @@ dogetch(void) } else if (c == KEY_LF) { - // XXX it is reported that still some users - // experience double ENTERs. We are not sure if there - // are still any stupid clients. But if any, you - // can reject the LFs. According the the compatibility - // test, most clients send CR only so it should be fine. -#ifdef ACCEPT_LF - return KEY_ENTER; -#else return KEY_UNKNOWN; -#endif } - // XXX also treat ^H and 127 (KEY_BS2) the same one? - // else if (c == KEY_BS2) - // { - // return KEY_BS; - // } - return c; } } @@ -400,148 +377,18 @@ _debug_check_keyinput() static int water_which_flag = 0; -#ifndef vkey -int -vkey(void) -{ - return igetch(); -} -#endif - -int -igetch(void) +static int +process_pager_keys(int ch) { - register int ch, mode = 0, last = 0; - while (1) { - ch = dogetch(); - - /* for escape codes, check - * http://support.dell.com/support/edocs/systems/pe2650/en/ug/5g387ad0.htm - */ - if (mode == 0 && ch == KEY_ESC) - mode = 1; - else if (mode == 1) { - - /* Escape sequence */ - - if (ch == '[' || ch == 'O') - { mode = 2; last = ch; } - else { - KEY_ESC_arg = ch; - return KEY_ESC; - } - - } - else if (mode == 2) - { - /* ^[ or ^O, - * ordered by frequency */ - - if(ch >= 'A' && ch <= 'D') /* Cursor key */ - { - return KEY_UP + (ch - 'A'); - } - else if (ch >= '1' && ch <= '6') /* Ins Del Home End PgUp PgDn */ - { - mode = 3; last = ch; - continue; - } - else if(ch == 'O') - { - mode = 4; - continue; - } - else if(ch == 'Z') - { - return KEY_STAB; - } - else if (ch == '0') - { - if (dogetch() == 'Z') - return KEY_STAB; - else - return KEY_UNKNOWN; - } - else if (last == 'O') { - /* ^[O ... */ - if (ch >= 'A' && ch <= 'D') - return KEY_UP + (ch - 'A'); - if (ch >= 'P' && ch <= 'S') // vt100 k1-4 - return KEY_F1 + (ch - 'P'); - if (ch >= 'T' && ch <= '[') // putty vt100+ F5-F12 - return KEY_F5 + (ch - 'T'); - if (ch >= 't' && ch <= 'z') // vt100 F5-F11 - return KEY_F5 + (ch - 't'); - if (ch >= 'p' && ch <= 's') // Old (num or fn)kbd 4 keys - return KEY_F1 + (ch - 'p'); - else if (ch == 'a') // DELL spec - return KEY_F12; - } - else return KEY_UNKNOWN; - } - else if (mode == 3) - { - /* ^[[1-6] */ - - /* ~: Ins Del Home End PgUp PgDn */ - if(ch == '~') - { - // vt220 style - if (last >= '1' && last <= '6') - return KEY_HOME + (last - '1'); - // else, unknown. - return KEY_UNKNOWN; - } - else if (last == '1') - { - if (ch >= '1' && ch <= '6') - { - // use num_in_buf() to prevent waiting keys - if (num_in_buf() && dogetch() == '~') /* must be '~' */ - return KEY_F1 + ch - '1'; - } - else if (ch >= '7' && ch <= '9') - { - // use num_in_buf() to prevent waiting keys - if (num_in_buf() && dogetch() == '~') /* must be '~' */ - return KEY_F6 + ch - '7'; - } - return KEY_UNKNOWN; - } - else if (last == '2') - { - if (ch >= '0' && ch <= '4') - { - // use inbuf() to prevent waiting keys - if (num_in_buf() && dogetch() == '~') /* hope you are '~' */ - return KEY_F9 + ch - '0'; - } - return KEY_UNKNOWN; - } - // if fall here, then escape sequence is broken. - return KEY_UNKNOWN; - } - else // here is switch for default keys - switch (ch) { // XXX: indent error -#ifdef DEBUG - case Ctrl('Q'):{ - struct rusage ru; - getrusage(RUSAGE_SELF, &ru); - vmsgf("sbrk: %d KB, idrss: %d KB, isrss: %d KB", - ((int)sbrk(0) - 0x8048000) / 1024, - (int)ru.ru_idrss, (int)ru.ru_isrss); - } - continue; -#endif - case Ctrl('L'): - redrawwin(); - refresh(); - continue; - - case Ctrl('U'): - if (currutmp != NULL && currutmp->mode != EDITING - && currutmp->mode != LUSERS && currutmp->mode) { - + assert(currutmp); + switch (ch) + { + case Ctrl('U') : + if ( currutmp->mode == EDITING || + currutmp->mode == LUSERS || + !currutmp->mode) { + return ch; + } else { screen_backup_t old_screen; int oldroll = roll; int my_newfd; @@ -555,147 +402,218 @@ igetch(void) i_newfd = my_newfd; roll = oldroll; scr_restore(&old_screen); - continue; - } - return ch; - case KEY_TAB: - if (PAGER_UI_IS(PAGER_UI_ORIG) || PAGER_UI_IS(PAGER_UI_NEW)) - if (currutmp != NULL && watermode > 0) { - check_water_init(); - watermode = (watermode + water_which->count) - % water_which->count + 1; - t_display_new(); - continue; - } - return ch; + } + return KEY_INCOMPLETE; + // TODO 醜死了的 code ,等好心人 refine case Ctrl('R'): - if (currutmp == NULL) - return (ch); - if (currutmp->msgs[0].pid && - PAGER_UI_IS(PAGER_UI_OFO) && wmofo == NOTREPLYING) { + if (PAGER_UI_IS(PAGER_UI_OFO)) + { int my_newfd; screen_backup_t old_screen; + if (!currutmp->msgs[0].pid || + wmofo != NOTREPLYING) + break; + scr_dump(&old_screen); my_newfd = i_newfd; i_newfd = 0; my_write2(); - scr_restore(&old_screen); + scr_restore(&old_screen); i_newfd = my_newfd; - continue; - } else if (!PAGER_UI_IS(PAGER_UI_OFO)) { - check_water_init(); - if (watermode > 0) { - watermode = (watermode + water_which->count) - % water_which->count + 1; - t_display_new(); - continue; - } else if (currutmp->mode == 0 && + return KEY_INCOMPLETE; + + } + + // non-UFO + check_water_init(); + + if (watermode > 0) + { + watermode = (watermode + water_which->count) + % water_which->count + 1; + t_display_new(); + return KEY_INCOMPLETE; + } + else if (watermode == 0 && + currutmp->mode == 0 && (currutmp->chatid[0] == 2 || currutmp->chatid[0] == 3) && - water_which->count != 0 && watermode == 0) { - /* 第二次按 Ctrl-R */ - watermode = 1; - t_display_new(); - continue; - } else if (watermode == -1 && currutmp->msgs[0].pid) { - /* 第一次按 Ctrl-R (必須先被丟過水球) */ - screen_backup_t old_screen; - int my_newfd; - scr_dump(&old_screen); - - /* 如果正在talk的話先不處理對方送過來的封包 (不去select) */ - my_newfd = i_newfd; - i_newfd = 0; - show_call_in(0, 0); - watermode = 0; + water_which->count != 0) + { + /* 第二次按 Ctrl-R */ + watermode = 1; + t_display_new(); + return KEY_INCOMPLETE; + } + else if (watermode == -1 && + currutmp->msgs[0].pid) + { + /* 第一次按 Ctrl-R (必須先被丟過水球) */ + screen_backup_t old_screen; + int my_newfd; + scr_dump(&old_screen); + + /* 如果正在talk的話先不處理對方送過來的封包 (不去select) */ + my_newfd = i_newfd; + i_newfd = 0; + show_call_in(0, 0); + watermode = 0; #ifndef PLAY_ANGEL - my_write(currutmp->msgs[0].pid, "水球丟過去: ", - currutmp->msgs[0].userid, WATERBALL_GENERAL, NULL); + my_write(currutmp->msgs[0].pid, "水球丟過去: ", + currutmp->msgs[0].userid, WATERBALL_GENERAL, NULL); #else - switch (currutmp->msgs[0].msgmode) { - case MSGMODE_TALK: - case MSGMODE_WRITE: - my_write(currutmp->msgs[0].pid, "水球丟過去: ", - currutmp->msgs[0].userid, WATERBALL_GENERAL, NULL); - break; - case MSGMODE_FROMANGEL: - my_write(currutmp->msgs[0].pid, "再問他一次: ", - currutmp->msgs[0].userid, WATERBALL_ANGEL, NULL); - break; - case MSGMODE_TOANGEL: - my_write(currutmp->msgs[0].pid, "回答小主人: ", - currutmp->msgs[0].userid, WATERBALL_ANSWER, NULL); - break; - } + switch (currutmp->msgs[0].msgmode) { + case MSGMODE_TALK: + case MSGMODE_WRITE: + my_write(currutmp->msgs[0].pid, "水球丟過去: ", + currutmp->msgs[0].userid, WATERBALL_GENERAL, NULL); + break; + case MSGMODE_FROMANGEL: + my_write(currutmp->msgs[0].pid, "再問他一次: ", + currutmp->msgs[0].userid, WATERBALL_ANGEL, NULL); + break; + case MSGMODE_TOANGEL: + my_write(currutmp->msgs[0].pid, "回答小主人: ", + currutmp->msgs[0].userid, WATERBALL_ANSWER, NULL); + break; + } #endif - i_newfd = my_newfd; + i_newfd = my_newfd; - /* 還原螢幕 */ - scr_restore(&old_screen); - continue; - } + /* 還原螢幕 */ + scr_restore(&old_screen); + return KEY_INCOMPLETE; } - return ch; + break; + + case KEY_TAB: + if (watermode <= 0 || + (!PAGER_UI_IS(PAGER_UI_ORIG) || PAGER_UI_IS(PAGER_UI_NEW))) + break; + + check_water_init(); + watermode = (watermode + water_which->count) + % water_which->count + 1; + t_display_new(); + return KEY_INCOMPLETE; case Ctrl('T'): - if (PAGER_UI_IS(PAGER_UI_ORIG) || PAGER_UI_IS(PAGER_UI_NEW)) { - if (watermode > 0) { - check_water_init(); - if (watermode > 1) - watermode--; - else - watermode = water_which->count; - t_display_new(); - continue; - } - } - return ch; + if (watermode <= 0 || + !(PAGER_UI_IS(PAGER_UI_ORIG) || PAGER_UI_IS(PAGER_UI_NEW))) + break; + + check_water_init(); + if (watermode > 1) + watermode--; + else + watermode = water_which->count; + t_display_new(); + return KEY_INCOMPLETE; case Ctrl('F'): - if (PAGER_UI_IS(PAGER_UI_NEW)) { - if (watermode > 0) { - check_water_init(); - if (water_which_flag == (int)water_usies) - water_which_flag = 0; - else - water_which_flag = - (water_which_flag + 1) % (int)(water_usies + 1); - if (water_which_flag == 0) - water_which = &water[0]; - else - water_which = swater[water_which_flag - 1]; - watermode = 1; - t_display_new(); - continue; - } - } - return ch; + if (watermode <= 0 || !PAGER_UI_IS(PAGER_UI_NEW)) + break; + + check_water_init(); + if (water_which_flag == (int)water_usies) + water_which_flag = 0; + else + water_which_flag = + (water_which_flag + 1) % (int)(water_usies + 1); + if (water_which_flag == 0) + water_which = &water[0]; + else + water_which = swater[water_which_flag - 1]; + watermode = 1; + t_display_new(); + return KEY_INCOMPLETE; case Ctrl('G'): - if (PAGER_UI_IS(PAGER_UI_NEW)) { - if (watermode > 0) { - check_water_init(); - water_which_flag = (water_which_flag + water_usies) % (water_usies + 1); - if (water_which_flag == 0) - water_which = &water[0]; - else - water_which = swater[water_which_flag - 1]; - watermode = 1; - t_display_new(); - continue; + if (watermode <= 0 || !PAGER_UI_IS(PAGER_UI_NEW)) + break; + + check_water_init(); + water_which_flag = (water_which_flag + water_usies) % (water_usies + 1); + if (water_which_flag == 0) + water_which = &water[0]; + else + water_which = swater[water_which_flag - 1]; + + watermode = 1; + t_display_new(); + return KEY_INCOMPLETE; + } + return ch; +} + +#ifndef vkey +int +vkey(void) +{ + return igetch(); +} +#endif + +// virtual terminal keyboard context +static VtkbdCtx vtkbd_ctx; + +int +igetch(void) +{ + register int ch; + + while (1) + { + ch = dogetch(); + + // convert virtual terminal keys + ch = vtkbd_process(ch, &vtkbd_ctx); + switch(ch) + { + case KEY_INCOMPLETE: + // XXX what if endless? + continue; + + case KEY_ESC: + KEY_ESC_arg = vtkbd_ctx.esc_arg; + return ch; + + case KEY_UNKNOWN: + return ch; + + // common global hot keys... + case Ctrl('L'): + redrawwin(); + refresh(); + continue; +#ifdef DEBUG + case Ctrl('Q'): + { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + vmsgf("sbrk: %d KB, idrss: %d KB, isrss: %d KB", + ((int)sbrk(0) - 0x8048000) / 1024, + (int)ru.ru_idrss, (int)ru.ru_isrss); } - } - return ch; + continue; +#endif + } - default: - return ch; + // complex pager hot keys + if (currutmp) + { + ch = process_pager_keys(ch); + if (ch == KEY_INCOMPLETE) + continue; } + + return ch; } // should not reach here. just to make compiler happy. - return 0; + return ch; } /* diff --git a/mbbsd/name.c b/mbbsd/name.c index 67381b00..803434b8 100644 --- a/mbbsd/name.c +++ b/mbbsd/name.c @@ -108,7 +108,7 @@ nc_cb_peek(int key, VGET_RUNTIME *prt, void *instance) return VGETCB_NEXT; break; - case KEY_BS2: case KEY_BS: /* backspace */ + case KEY_BS: /* backspace */ nc_int->dirty = -1; break; @@ -336,7 +336,7 @@ generalnamecomplete(const char *prompt, char *data, int len, size_t nmemb, } continue; - } else if (ch == KEY_BS2 || ch == KEY_BS) { /* backspace */ + } else if (ch == KEY_BS) { /* backspace */ if (ptr == 0) continue; morelist = -1; diff --git a/mbbsd/register.c b/mbbsd/register.c index 81d13cd0..5123f63b 100644 --- a/mbbsd/register.c +++ b/mbbsd/register.c @@ -2508,8 +2508,9 @@ regform2_validate_page(int dryrun) #endif case 's': // skip case 'd': // delete - case KEY_DEL: //delete - if (ch == KEY_DEL) ch = 'd'; + case Ctrl('D'): // delete + case KEY_DEL: // delete + if (ch == KEY_DEL || ch == Ctrl('D')) ch = 'd'; grayout(ci*2, ci*2+1, GRAYOUT_DARK); move_ansi(ci*2, 4); outc(ch); diff --git a/mbbsd/talk.c b/mbbsd/talk.c index 16be0d89..1e27257a 100644 --- a/mbbsd/talk.c +++ b/mbbsd/talk.c @@ -1328,7 +1328,6 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) // complex data change case KEY_BS: - case KEY_BS2: if (twin->curcol > 0) { int delta = 1; @@ -1347,6 +1346,7 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) } return; + case KEY_DEL: case Ctrl('D'): if (twin->curcol < line->len) { diff --git a/mbbsd/visio.c b/mbbsd/visio.c index 4f6909d8..38efcbc9 100644 --- a/mbbsd/visio.c +++ b/mbbsd/visio.c @@ -1065,7 +1065,7 @@ vgetstring(char *_buf, int len, int flags, const char *defstr, const VGET_CALLBA } break; - case KEY_BS: case KEY_BS2: + case KEY_BS: if (rt.icurr > 0) { // kill previous one charracter. memmove(buf+rt.icurr-1, buf+rt.icurr, rt.iend-rt.icurr+1); diff --git a/mbbsd/vtkbd.c b/mbbsd/vtkbd.c new file mode 100644 index 00000000..8a00671f --- /dev/null +++ b/mbbsd/vtkbd.c @@ -0,0 +1,279 @@ +/* + * 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 <piaip@csie.org> + * 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(<X]), and Delete keys are special due to history... + * - on vt220, BS=0x7F, Delete=ESC[3~ (screen/xterm/PuTTY) + * - on vt100, BS=0x7F + * - on vt100/xterm, BS=0x08, Delete=0x7F (Windows/VMX/DOS telnet) + * So we define + * KEY_BS = BACKSPACE/ERASE = 0x08, 0x7F + * KEY_DEL = DELETE = ESC[3~ + * + * * CR/LF Rules + * The traditional newline maps to following combination... + * - UNIX, LF + * - Win, CR+LF + * - Mac, CR + * When it comes to terminal,CR LF should be treated as one. + * There were reports that some users getting double ENTERs if we take LF + * as ENTER, so we decided reject LF. We are not sure if there is any + * clients sending LF only, but according the the compatibility test, + * most modern clients send only CR (or CR+LF). + * So we define + * KEY_CR = CR, CR+LF + * KEY_LF = ignored. + * --------------------------------------------------------------------------- + * * The complete list to support: + * - Up/Down/Right/Left: <Esc> [ <A-D> | <Esc> O <A-D> (app) + * - Home/Ins/Del/End/PgUp/PgDn: <Esc> [ <1~6> ~ + * - Shift-TAB: <Esc> [ Z | <Esc> [ 0 Z + * - F1~F4: <Esc> [ 1 <1234> ~ | <Esc> O <PQRS> + * - F5: <Esc> [ 1 <5> ~ + * - F6-F8: <Esc> [ 1 <789> ~ + * - F9-F12: <Esc> [ 2 <0134> ~ + * - Num 0-9 *+,-./=ENTER: <Esc> O <pqrstuvwxyjklmnoXM> + * + * Note: we don't support some rare terms like <Esc> O <TUVWXYZA> 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, // <Esc> + VKSTATE_ESC_APP, // <Esc> O + VKSTATE_ESC_QUOTE, // <Esc> [ + VKSTATE_ZERO, // <Esc> [ 0 (wait Z) + VKSTATE_ONE, // <Esc> [ <1> + VKSTATE_TWO, // <Esc> [ <2> + VKSTATE_TLIDE, // <Esc> [ * (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: // <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: // <Esc> 0 + if (c != 'Z') + break; + + ctx->state = VKSTATE_NORMAL; + return KEY_STAB; + + case VKSTATE_ESC_APP: // <Esc> 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: // <Esc> [ + + 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: // <Esc> [ 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: // <Esc> [ 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 |