который при поступлении кода 033 начинает ожидать составную последовательность - мы
должны выставлять таймаут, например alarm(1); и если по его истечении больше никаких
символов не поступило - выдавать код 033 как код клавиши Esc.
Напишите распознаватель кодов, поступающих с клавиатуры. Коды обычных букв выда-
вать как есть (0..0377), коды функциональных клавиш выдавать как числа >= 0400.
Учтите, что разные типы дисплеев посылают разные последовательности от одних и тех же
функциональных клавиш: предусмотрите настройку на систему команд ДАННОГО дисплея при
помощи библиотеки termcap. Распознаватель удобно строить при помощи сравнения посту-
пающих символов с ветвями дерева (спускаясь по нужной ветви дерева при поступлении
очередного символа. Как только достигли листа дерева - возвращаем код, приписанный
этому листу):
---> '\033' ---> '[' ---> 'A' --> выдать 0400
| \--> 'B' --> 0401
| \--> 'C' --> 0402
| \--> 'D' --> 0403
\--> 'X' -----------> 0404
...
Нужное дерево стройте при настройке на систему команд данного дисплея.
Библиотека curses уже имеет такой встроенный распознаватель. Чтобы составные
последовательности склеивались в специальные коды, вы должны установить режим keypad:
int c; WINDOW *window;
...
keypad(window, TRUE);
...
c = wgetch(window);
Без этого wgetch() считывает все символы поодиночке. Символические названия кодов
для функциональных клавиш перечислены в и имеют вид KEY_LEFT, KEY_RIGHT
и.т.п. Если вы работаете с единственным окном размером с весь экран, то в качестве
параметра window вы должны использовать стандартное окно stdscr (это имя предопреде-
лено в include-файле curses.h).
# ======================================== Makefile для getch
getch: getch.o
cc getch.o -o getch -ltermlib
getch.o: getch.c getch.h
cc -g -DUSG -c getch.c
А. Богатырев, 1992-95 - 372 - Си в UNIX
/* Разбор составных последовательностей клавиш с клавиатуры. */
/* ================================================== getch.h */
#define FALSE 0
#define TRUE 1
#define BOOLEAN unsigned char
#define INPUT_CHANNEL 0
#define OUTPUT_CHANNEL 1
#define KEY_DOWN 0400
#define KEY_UP 0401
#define KEY_LEFT 0402
#define KEY_RIGHT 0403
#define KEY_PGDN 0404
#define KEY_PGUP 0405
#define KEY_HOME 0406
#define KEY_END 0407
#define KEY_BACKSPACE 0410
#define KEY_BACKTAB 0411
#define KEY_DC 0412
#define KEY_IC 0413
#define KEY_DL 0414
#define KEY_IL 0415
#define KEY_F(n) (0416+n)
#define ESC ' 33'
extern char *tgetstr();
void _put(char c);
void _puts(char *s);
void keyboard_access_denied(void);
char *strdup(const char *s);
void keyinit(void);
int getc_raw(void);
void keyreset(void);
int getch(void);
int lgetch(BOOLEAN);
int ggetch(BOOLEAN);
int kgetch(void);
void _sigalrm(int n);
void init_keytry(void);
void add_to_try(char *str, short code);
void keypad_on(void);
void keypad_off(void);
int dotest(void);
void tinit(void);
void main(void);
А. Богатырев, 1992-95 - 373 - Си в UNIX
/* ===================================================== getch.c
* The source version of getch.c file was
* written by Pavel Curtis.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include "getch.h"
#define keypad_local S[0]
#define keypad_xmit S[1]
#define key_backspace S[2]
#define key_backtab S[3]
#define key_left S[4]
#define key_right S[5]
#define key_up S[6]
#define key_down S[7]
#define key_ic S[8]
#define key_dc S[9]
#define key_il S[10]
#define key_dl S[11]
#define key_f1 S[12]
#define key_f2 S[13]
#define key_f3 S[14]
#define key_f4 S[15]
#define key_f5 S[16]
#define key_f6 S[17]
#define key_f7 S[18]
#define key_f8 S[19]
#define key_f9 S[20]
#define key_f10 S[21] /* f0 */
#define key_f11 S[22] /* f11 */
#define key_f12 S[23] /* f12 */
#define key_home S[24]
#define key_end S[25]
#define key_npage S[26]
#define key_ppage S[27]
#define TOTAL 28
А. Богатырев, 1992-95 - 374 - Си в UNIX
/* descriptors for keys */
char *KEYS[TOTAL+1] = {
"ke", "ks",
"kb", "kB",
"kl", "kr", "ku", "kd",
"kI", "kD", "kA", "kL",
"f1", "f2", "f3", "f4", "f5",
"f6", "f7", "f8", "f9", "f0",
"f.", "f-",
"kh", "kH", "kN", "kP",
NULL
}, *S[TOTAL];
void _put (char c) { write( INPUT_CHANNEL, &c, 1 ); }
void _puts(char *s) { tputs ( s, 1, _put ); }
static int _backcnt = 0;
static char _backbuf[30];
static struct try {
struct try *child;
struct try *sibling;
char ch;
short value;
} *_keytry;
BOOLEAN keypadok = FALSE;
struct termios new_modes;
void keyboard_access_denied(){ printf( "Клавиатура недоступна.\n" ); exit(1); }
char *strdup(const char *s) { return strcpy((char *) malloc(strlen(s)+1), s); }
А. Богатырев, 1992-95 - 375 - Си в UNIX
/* Инициализация таблицы строк */
void keyinit(){
char *key, nkey[80], *p;
register i;
keyreset();
for( i=0; i < TOTAL; i++ ){
p = nkey;
printf("tgetstr(%s)...", KEYS[i]);
key = tgetstr(KEYS[i], &p);
if(S[i]) free(S[i]);
if(key == NULL){
S[i] = NULL; /* No such key */
printf("клавиша не определена.\n");
}else{
/* Decrypted string */
S[i] = strdup(key);
printf("считано.\n");
}
}
init_keytry();
if( tcgetattr(INPUT_CHANNEL, &new_modes) < 0 ){
keyboard_access_denied();
}
/* input flags */
/* отменить преобразование кода '\r' в '\n' на вводе */
new_modes.c_iflag &= ~ICRNL;
if ((new_modes.c_cflag & CSIZE) == CS8) /* 8-битный код */
new_modes.c_iflag &= ~ISTRIP; /* отменить & 0177 на вводе */
/* output flags */
/* отменить TAB3 - замену табуляций '\t' на пробелы */
/* отменить ONLCR - замену '\n' на пару '\r\n' на выводе */
new_modes.c_oflag &= ~(TAB3 | ONLCR);
/* local flags */
/* выключить режим ICANON, включить CBREAK */
/* выключить эхоотображение набираемых символов */
new_modes.c_lflag &= ~(ICANON | ECHO);
/* control chars */ /* при вводе с клавиш ждать не более ... */
new_modes.c_cc[VMIN] = 1; /* 1 символа и */
new_modes.c_cc[VTIME] = 0; /* 0 секунд */
/* Это соответствует режиму CBREAK */
/* Символы, нажатие которых заставляет драйвер терминала послать сигнал
* либо отредактировать набранную строку. Значение 0 означает,
* что соответствующего символа не будет */
new_modes.c_cc[VINTR] = '\0'; /* символ, генерящий SIGINT */
new_modes.c_cc[VQUIT] = '\0'; /* символ, генерящий SIGQUIT */
new_modes.c_cc[VERASE] = '\0'; /* забой (отмена последнего символа)*/
new_modes.c_cc[VKILL] = '\0'; /* символ отмены строки */
}
А. Богатырев, 1992-95 - 376 - Си в UNIX
/* Чтение одного символа непосредственно с клавиатуры */
int getc_raw(){
int n; char c;
n = read(INPUT_CHANNEL, &c, 1);
if (n <= 0) return EOF;
return (c & 0xFF);
}
static BOOLEAN _getback = FALSE;
static char _backchar = '\0';
/* Чтение символа - либо из буфера (если не пуст), либо с клавиатуры */
#define nextc() (_backcnt > 0 ? _backbuf[--_backcnt] : \
_getback ? _getback = FALSE, _backchar : \
getc_raw())
#define putback(ch) _backbuf[_backcnt++] = ch
void keyreset(){
_backcnt = 0; _backchar = '\0';
_getback = FALSE;
}
/* Функция чтения составного символа */
int getch(){
int c = lgetch(TRUE);
keypad_off();
return c;
}
/*
ВНИМАНИЕ!
Если в процессе будет получен сигнал,
в то время как процесс находится внутри вызова getch(),
то системный вызов read() вернет 0 и errno == EINTR.
В этом случае getch() вернет '\0'.
Чтобы избежать этой ситуации используется функция lgetch()
*/
int lgetch(BOOLEAN kpad) {
int c;
while((c = ggetch(kpad)) <= 0);
return c;
}
int ggetch(BOOLEAN kpad) {
int kgetch();
if( kpad ) keypad_on();
else keypad_off();
return keypadok ? kgetch() : nextc();
}
А. Богатырев, 1992-95 - 377 - Си в UNIX
/*
** int kgetch()
**
** Get an input character, but take care of keypad sequences, returning
** an appropriate code when one matches the input. After each character
** is received, set a one-second alarm call. If no more of the sequence
** is received by the time the alarm goes off, pass through the sequence
** gotten so far.
**
*/
#define CRNL(c) (((c) == '\r') ? '\n' : (c))
/* борьба с русской клавиатурой */
#if !defined(XENIX) || defined(VENIX)
# define unify(c) ( (c)&(( (c)&0100 ) ? ~0240 : 0377 ))
#else
# define unify(c) (c)
#endif
А. Богатырев, 1992-95 - 378 - Си в UNIX
/* ==================================================================== */
#if !defined(XENIX) && !defined(USG) && !defined(M_UNIX) && !defined(unix)
/* Для семейства BSD */
static BOOLEAN alarmed;
jmp_buf jbuf;
int kgetch()
{
register struct try *ptr;
int ch;
char buffer[10]; /* Assume no sequences longer than 10 */
register char *bufp = buffer;
void (*oldsig)();
void _sigalrm();
ptr = _keytry;
oldsig = signal(SIGALRM, _sigalrm);
alarmed = FALSE;
if( setjmp( jbuf )) /* чтоб свалиться сюда с read-а */