Библиотека сайта rus-linux.net
| Цилюрик О.И. Linux-инструменты для Windows-программистов | ||
| Назад | Библиотеки API POSIX | Вперед |
Терминал, режим ввода: канонический и некононический
Частным случаем блокирующего-неблокирующего вывода
является ввод с терминала — часто задаваемый вопрос: как
реализовать неблокирующий посимвольный ввод с терминала/консоли
(такой режим часто используется, например, визуальными редакторами)?
Какие вызовы API для этого использовать? Ответ состоит в том, что для
неблокирующего ввода не существует какого-то специального набора
вызовов POSIX, а используется соответствующий набор параметров
терминала, и, в частности, канонический или неканонический режим
ввода. Текущие установленные параметры терминала можно посмотреть
(наиболее интересующие нас параметры в контексте данного
рассмотрения: icanon, echo, min, time):
$ stty -a < /dev/tty speed 38400 baud; rows 33; columns 93; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts -ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8 opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
Примечание: Вся терминальная система (и команда stty)
реализована в те давние времена, когда в большинстве случаев
подключение терминала производилось через последовательные линии
RS-232, поэтому очень много параметров ориентированы на параметры
такой линии. Но, если возникает необходимость работать с RS-232 (для
связи с устройствами), то оказывается полезной и ещё одна команда,
вот как она возвращает, например, диагностику:
$ sudo setserial -bg /dev/ttyS* /dev/ttyS0 at 0x03f8 (irq = 4) is a 16550A /dev/ttyS1 at 0x02f8 (irq = 3) is a 16550A
Режим обмена (то, что мы видели по команде stty)
с терминалом описывается (<bits/termios.h>)
программной структурой:
#define NCCS 32
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
Основные функции (<termios.h>)
работы с режимами терминала:
int tcgetattr( int fd, struct termios *termios );
int tcsetattr( int fd, int optional_actions, const struct termios *termios );
- где optional_actionsуказывает как поступать с вводом и выводом, уже
поставленным в очередь. Это может быть (<bits/termios.h>)
одно из следующих значений:
/* tcsetattr uses these */ #define TCSANOW 0 #define TCSADRAIN 1 #define TCSAFLUSH 2
TCSANOW - делать изменения немедленно;
TCSADRAIN - делать изменения после ожидания, пока весь поставленный в очередь
вывод не выведен (обычно используется при изменении параметров,
которые воздействуют на вывод);
TCSAFLUSH - подобен TCSADRAIN, но отбрасывает любой поставленный в очередь ввод.
Этой информации достаточно, чтобы рассмотреть
следующий пример (архив terminal.tgz):
прямое управление курсором экрана терминала (нажатием клавиш 'd',
'u', 'l', 'r' — вверх, вниз, влево, вправо, соответственно, 'q'
— выход из программы):
move.c :
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
int main ( int argc, char **argv ) {
struct termios savetty, tty;
char ch;
int x, y;
printf( "Enter start position (x y): " );
scanf( "%d %d", &x, &y );
if( !isatty( 0 ) ) {
fprintf( stderr, "stdin not terminal\n" );
exit( EXIT_FAILURE );
};
tcgetattr( 0, &tty ); // получили состояние терминала
savetty = tty;
tty.c_lflag &= ~( ICANON | ECHO | ISIG );
tty.c_cc[ VMIN ] = 1;
tcsetattr( 0, TCSAFLUSH, &tty ); // изменили состояние терминала
printf( "%c[2J", 27 ); // очистили экран
fflush( stdout );
printf( "%c[%d;%dH", 27, y, x ); // установили курсор в позицию
fflush( stdout );
for( ; ; ) {
read( 0, &ch, 1 );
if( ch == 'q' ) break;
switch( ch ) {
case 'u':
printf( "%c[1A", 27 );
break;
case 'd':
printf( "%c[1B", 27 );
break;
case 'r':
printf( "%c[1C", 27 );
break;
case 'l':
printf( "%c[1D", 27 );
break;
};
fflush( stdout );
};
tcsetattr( 0, TCSAFLUSH, &savetty ); // восстановили состояние терминала
printf( "\n" );
exit( EXIT_SUCCESS );
}
Примечание: Обязательная часть
подобных программ (не показанная в примере, чтобы его не усложнять)
это перехват сигналов завершения (SIGINT, SIGTERM) для
восстановления перед завершением программы канонического режима
ввода; в противном случае терминал будет «испорчен» для
дальнейшего использования в качестве терминала. Для восстановления
режима с успехом может быть использован вызов atexit(),
как это сделано в примере (в том же архиве):
ncan.c :
...
struct termios saved_attributes;
void reset_input_mode( void ) {
tcsetattr( STDIN_FILENO, TCSANOW, &saved_attributes);
}
void set_input_mode( void ) {
struct termios tattr;
...
tcgetattr( STDIN_FILENO, &saved_attributes );
atexit( reset_input_mode );
...
tcsetattr( STDIN_FILENO, TCSAFLUSH, &tattr );
}
...
| Предыдущий раздел: | Оглавление | Следующий раздел: |
| Асинхронный ввод-вывод | Источники информации |
