Неканонический режим работы терминала linux

Глава 5

В этой главе вы познакомитесь с некоторыми улучшениями, которые вам, возможно, захочется внести в базовое приложение из главы 2. Его, быть может, самый очевидный недостаток — пользовательский интерфейс; он достаточно функционален, но не слишком элегантен. Теперь вы узнаете, как сделать более управляемым терминал пользователя, т. е. ввод с клавиатуры и вывод на экран. Помимо этого вы научитесь обеспечивать написанным вами программам возможность получения вводимых данных от пользователя даже при наличии перенаправления ввода и гарантировать вывод данных в нужное место на экране.

Несмотря на то, что заново реализованное приложение для управления базой данных компакт-дисков не увидит свет до конца главы 7, его основы вы заложите в этой главе. Глава 6 посвящена curses, которые представляют собой вовсе не древнее проклятие, а библиотеку функций, предлагающих программный код высокого уровня для управления отображением на экране терминала. Попутно вы узнаете чуть больше о размышлениях прежних профи UNIX, познакомившись с основными принципами систем Linux и UNIX и понятием терминала. Низкоуровневый доступ, представленный в этой главе, быть может именно то, что вам нужно. Большая часть того, о чем мы пишем здесь, хорошо подходит для программ, выполняющихся в окне консоли, таких как эмуляторы терминала KDE’s Konsole, GNOME’s gnome-terminal или стандартный X11 xterm.

В этой главе вы, в частности, узнаете о:

□ чтении с терминала и записи на терминал;

□ драйверах терминала и общем терминальном интерфейсе (General Terminal Interface, GTI);

□ выводе терминала и базе данных

□ обнаружении нажатия клавиш.

Чтение с терминала и запись на терминал

В главе 3 вы узнали, что, когда программа запускается из командной строки, оболочка обеспечивает присоединение к ней стандартных потоков ввода и вывода. Вы получаете возможность взаимодействия с пользователем простым применением подпрограмм

В упражнении 5.1 в программе menu1.c вы попытаетесь переписать на языке С подпрограммы формирования меню, использующие только эти две функции.

Упражнение 5.1. Подпрограммы формирования меню на языке C

1. Начните со следующих строк, определяющих массив, который будет использоваться как меню, и прототип (описание) функции

3. Теперь важный фрагмент кода — функция, которая и выводит на экран меню и считывает ввод пользователя:

и просит пользователя ввести первый символ выбранного пункта. Далее выполняется цикл до тех пор, пока функция

Читайте также:  Windows 10 консоль linux

Когда вы откомпилируете и выполните программу, то обнаружите, что она ведет себя не так, как ожидалось. Для того чтобы продемонстрировать возникающую проблему, далее приведен вариант диалога на экране терминала.

Для того чтобы сделать выбор, пользователь должен последовательно нажать клавиши , , , . Здесь возникают, как минимум, две проблемы; самая серьезная заключается в том, что вы получаете сообщение «Incorrect choice» («Неверный выбор») после каждого корректного выбора. Кроме того, вы еще должны нажать клавишу (или ), прежде чем программа считает введенные данные.

Сравнение канонического и неканонического режимов

Обе эти проблемы тесно связаны. По умолчанию ввод терминала не доступен программе до тех пор, пока пользователь не нажмет клавишу или . В большинстве случаев это достоинство, поскольку данный способ позволяет пользователю корректировать ошибки набора с помощью клавиш или . Только когда он остается доволен увиденным на экране, пользователь нажимает клавишу , чтобы ввод стал доступен программе.

Такое поведение называется каноническим или стандартным режимом. Весь ввод обрабатывается как последовательность строк. Пока строка ввода не завершена (обычно с помощью нажатия клавиши ), интерфейс терминала управляет всеми нажатыми клавишами, включая , и приложение не может считать ни одного символа.

Прямая противоположность — неканонический режим, в котором приложение получает больше возможностей контроля над обработкой вводимых символов. Мы еще вернемся к этим двум режимам немного позже в этой главе.

Помимо всего прочего, обработчик терминала в ОС Linux помогает превращать символы прерываний в сигналы (например, останавливающие выполнение программы, когда вы нажмете комбинацию клавиш +), он также может автоматически выполнить обработку нажатых клавиш и и вам не придется реализовывать ее в каждой написанной вами программе. О сигналах вы узнаете больше в главе 11.

Итак, что же происходит в данной программе? ОС Linux сохраняет ввод до тех пор, пока пользователь не нажмет клавишу , и затем передает в программу символ выбранного пункта меню и следом за ним код клавиши . Каждый раз, когда вы вводите символ пункта меню, программа вызывает функцию

Символ, который на самом деле видит программа, — это не символ ASCII возврата каретки CR (десятичный код 13, шестнадцатеричный 0D), а символ перевода строки LF (десятичный код 10, шестнадцатеричный 0A). Так происходит потому, что на внутреннем уровне ОС Linux (как и UNIX) всегда применяет перевод строки для завершения текстовых строк, т. е. в отличие от других ОС, таких как MS-DOS, использующих комбинацию символов возврата каретки и перевода строки, ОС UNIX применяет, для обозначения новой строки только символ перевода строки. Если вводное или выводное устройство посылает или запрашивает и символ возврата каретки, в ОС Linux об этом заботится обработчик терминала. Если вы привыкли работать в MS-DOS или других системах, это может показаться странным, но одно из существенных преимуществ заключается в отсутствии в ОС Linux реальной разницы между текстовыми и бинарными файлами. Символы возврата каретки обрабатываются, только когда вы вводите или выводите их на терминал или некоторые принтеры и плоттеры.

Читайте также:  Xrdp kali linux установка

Вы можете откорректировать основной недостаток вашей подпрограммы меню, просто игнорируя дополнительный символ перевода строки с помощью программного кода, подобного приведенному далее:

Он решает непосредственно возникшую проблему, и вы увидите вывод, подобный приведенному далее:

Источник

Неканонический режим работы терминала linux

Библиотека сайта rus-linux.net

Терминал, режим ввода: канонический и некононический

Частным случаем блокирующего-неблокирующего вывода является ввод с терминала — часто задаваемый вопрос: как реализовать неблокирующий посимвольный ввод с терминала/консоли (такой режим часто используется, например, визуальными редакторами)? Какие вызовы API для этого использовать? Ответ состоит в том, что для неблокирующего ввода не существует какого-то специального набора вызовов POSIX, а используется соответствующий набор параметров терминала, и, в частности, канонический или неканонический режим ввода. Текущие установленные параметры терминала можно посмотреть (наиболее интересующие нас параметры в контексте данного рассмотрения: icanon, echo, min, time ):

Примечание: Вся терминальная система (и команда 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 ) с терминалом описывается ( ) программной структурой:

#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 */ >;

Основные функции ( ) работы с режимами терминала:

int tcgetattr( int fd, struct termios *termios );

Читайте также:  Intel graphics opengl linux

int tcsetattr( int fd, int optional_actions, const struct termios *termios );

— где optional_actions указывает как поступать с вводом и выводом, уже поставленным в очередь. Это может быть ( ) одно из следующих значений:

/* 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 #include #include #include #include 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 ); >.

Источник

Оцените статью
Adblock
detector