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 | 85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb (patch) | |
tree | 67f1759802f97148f89ba77e083ea85f4de22e41 | |
parent | ae4ff7c9da0f5973cf64a21a3410e0ed246894e6 (diff) | |
download | pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.tar pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.tar.gz pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.tar.bz2 pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.tar.lz pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.tar.xz pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.tar.zst pttbbs-85b3ca2ccd6363ff3bfd8b5f075eb2e690dc47fb.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@4887 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
-rw-r--r-- | pttbbs/include/bbs.h | 1 | ||||
-rw-r--r-- | pttbbs/include/common.h | 33 | ||||
-rw-r--r-- | pttbbs/include/vtkbd.h | 83 | ||||
-rw-r--r-- | pttbbs/mbbsd/Makefile | 2 | ||||
-rw-r--r-- | pttbbs/mbbsd/edit.c | 3 | ||||
-rw-r--r-- | pttbbs/mbbsd/io.c | 472 | ||||
-rw-r--r-- | pttbbs/mbbsd/name.c | 4 | ||||
-rw-r--r-- | pttbbs/mbbsd/register.c | 5 | ||||
-rw-r--r-- | pttbbs/mbbsd/talk.c | 2 | ||||
-rw-r--r-- | pttbbs/mbbsd/visio.c | 2 | ||||
-rw-r--r-- | pttbbs/mbbsd/vtkbd.c | 279 |
11 files changed, 567 insertions, 319 deletions
diff --git a/pttbbs/include/bbs.h b/pttbbs/include/bbs.h index 888f6d74..d16d5b02 100644 --- a/pttbbs/include/bbs.h +++ b/pttbbs/include/bbs.h @@ -43,6 +43,7 @@ extern "C" { #endif #include "ansi.h" +#include "vtkbd.h" #include "visio.h" #include "statistic.h" #include "uflags.h" diff --git a/pttbbs/include/common.h b/pttbbs/include/common.h index d5400070..a2829047 100644 --- a/pttbbs/include/common.h +++ b/pttbbs/include/common.h @@ -154,39 +154,6 @@ #define ST_FRIEND (IBH | IFH | HFM) #define ST_REJECT (IRH | HRM) -/* 鍵盤設定 */ -#define KEY_TAB 9 -#define KEY_ESC 27 -#define KEY_CR ('\r') // Ctrl('M'), 0x0D -#define KEY_LF ('\n') // Ctrl('J'), 0x0A -#define KEY_ENTER KEY_CR // for backward compatibility -#define KEY_BS (Ctrl('H')) -#define KEY_BS2 (0x7f) -#define KEY_UP 0x0101 -#define KEY_DOWN 0x0102 -#define KEY_RIGHT 0x0103 -#define KEY_LEFT 0x0104 -#define KEY_STAB 0x0105 /* shift-tab */ -#define KEY_HOME 0x0201 -#define KEY_INS 0x0202 -#define KEY_DEL 0x0203 -#define KEY_END 0x0204 -#define KEY_PGUP 0x0205 -#define KEY_PGDN 0x0206 -#define KEY_F1 0x0301 -#define KEY_F2 0x0302 -#define KEY_F3 0x0303 -#define KEY_F4 0x0304 -#define KEY_F5 0x0305 -#define KEY_F6 0x0306 -#define KEY_F7 0x0307 -#define KEY_F8 0x0308 -#define KEY_F9 0x0309 -#define KEY_F10 0x030A -#define KEY_F11 0x030B -#define KEY_F12 0x030C -#define KEY_UNKNOWN 0x0FFF /* unknown sequence */ - #define QCAST int (*)(const void *, const void *) #define Ctrl(c) (c & 037) #define chartoupper(c) ((c >= 'a' && c <= 'z') ? c+'A'-'a' : c) diff --git a/pttbbs/include/vtkbd.h b/pttbbs/include/vtkbd.h new file mode 100644 index 00000000..f1e998c8 --- /dev/null +++ b/pttbbs/include/vtkbd.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef _VTKBD_H +#define _VTKBD_H + +/* context definition */ +typedef struct { + int state; + int esc_arg; +} VtkbdCtx; + +/* vtkbd API */ +int vtkbd_process(int c, VtkbdCtx *ctx); + +/* common ASCII compatible keys definition */ +#define KEY_TAB 9 +#define KEY_ESC 27 +#define KEY_CR ('\r') // Ctrl('M'), 0x0D +#define KEY_LF ('\n') // Ctrl('J'), 0x0A, will be ignored. +#define KEY_ENTER KEY_CR // for backward compatibility + +/* BS/ERASE/DEL, see vtkbd.c for the rules */ +#define KEY_BS (0x08) // see vtkbd.c for BS/DEL Rules + +/* arrow keys (must follow vt100 ordering) */ +#define KEY_UP 0x0101 +#define KEY_DOWN 0x0102 +#define KEY_RIGHT 0x0103 +#define KEY_LEFT 0x0104 + +#define KEY_STAB 0x0109 /* shift-tab */ + +/* 6 extended keys (must follow vt220 ordering) */ +#define KEY_HOME 0x0201 +#define KEY_INS 0x0202 +#define KEY_DEL 0x0203 +#define KEY_END 0x0204 +#define KEY_PGUP 0x0205 +#define KEY_PGDN 0x0206 + +/* PFn/Fn function keys */ +#define KEY_F1 0x0301 +#define KEY_F2 0x0302 +#define KEY_F3 0x0303 +#define KEY_F4 0x0304 +#define KEY_F5 0x0305 +#define KEY_F6 0x0306 +#define KEY_F7 0x0307 +#define KEY_F8 0x0308 +#define KEY_F9 0x0309 +#define KEY_F10 0x030A +#define KEY_F11 0x030B +#define KEY_F12 0x030C + +/* vtkbd meta keys */ +#define KEY_INCOMPLETE 0x0420 /* 0x?20 to prevent accident usage */ +#define KEY_UNKNOWN 0x0FFF /* unknown sequence */ + +#endif // _VTKBD_H + +// vim:ts=4:sw=4:et diff --git a/pttbbs/mbbsd/Makefile b/pttbbs/mbbsd/Makefile index 6b15e1dc..98335987 100644 --- a/pttbbs/mbbsd/Makefile +++ b/pttbbs/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/pttbbs/mbbsd/edit.c b/pttbbs/mbbsd/edit.c index f39280d4..676fc7de 100644 --- a/pttbbs/mbbsd/edit.c +++ b/pttbbs/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; diff --git a/pttbbs/mbbsd/io.c b/pttbbs/mbbsd/io.c index 68a2a62a..e0abbe31 100644 --- a/pttbbs/mbbsd/io.c +++ b/pttbbs/mbbsd/io.c @@ -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/pttbbs/mbbsd/name.c b/pttbbs/mbbsd/name.c index 67381b00..803434b8 100644 --- a/pttbbs/mbbsd/name.c +++ b/pttbbs/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/pttbbs/mbbsd/register.c b/pttbbs/mbbsd/register.c index 81d13cd0..5123f63b 100644 --- a/pttbbs/mbbsd/register.c +++ b/pttbbs/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/pttbbs/mbbsd/talk.c b/pttbbs/mbbsd/talk.c index 16be0d89..1e27257a 100644 --- a/pttbbs/mbbsd/talk.c +++ b/pttbbs/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/pttbbs/mbbsd/visio.c b/pttbbs/mbbsd/visio.c index 4f6909d8..38efcbc9 100644 --- a/pttbbs/mbbsd/visio.c +++ b/pttbbs/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/pttbbs/mbbsd/vtkbd.c b/pttbbs/mbbsd/vtkbd.c new file mode 100644 index 00000000..8a00671f --- /dev/null +++ b/pttbbs/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 |