Управление терминалом (1114938), страница 3
Текст из файла (страница 3)
Прикладная программа должна сама предоставлятьсредства для редактирования ввода, если это необходимо.Неканонический режим предоставляет специальные параметры MIN и TIME для управления тем, сколько ждать ввода символов, и нужно ли ждать вообще или возвращатьсянемедленно, даже если нет введённых символов. Параметры MIN и TIME хранятся в массиве c_cc. VMIN и VTIME — имена индексов соответствующих элементов массива.VMINИндекс значения MIN в массиве специальных символов. Значение MIN имеетсмысл только в неканоническом режиме ввода.
Оно задаёт минимальное количество байт в очереди ввода терминала, необходимое, чтобы read завершился.VTIME Индекс значения TIME в массиве специальных символов. Значение TIME имеетсмысл только в неканоническом режиме ввода. Оно задаёт максимальное время, которое read будет ждать до возврата из системного вызова.
Время задаётся в десятых долях секунды (то есть, 10 означает 1 секунду).Значения MIN и TIME вместе определяют критерий, когда системный вызов read завер7шает работу. Точный смысл зависит от того, какой из этих параметров не равен 0. Возможнычетыре случая.• И TIME и MIN не равны 0. В этом случае TIME задаёт, сколько времени после получения очередного введённого символа ждать, придёт ли ещё один символ. После того,как был считан первый символ, read ждёт, пока либо не поступят MIN байтов, либо завремя TIME не поступит ни одного нового символа.read всегда дожидается поступления первого символа, даже если интервал времениTIME закончится раньше. read может вернуть больше, чем MIN символов, если столько символов окажется в очереди ввода терминала.• И MIN, и TIME оба равны 0. В этом случае read всегда завершается немедленно, возвращая столько символов, столько в этот момент находится в очереди ввода терминала,но не больше чем число байт, заданное в параметре read.
Если очередь ввода пуста,read возвращает 0.• MIN равно 0, но TIME не равно 0. В этом случае read ждёт поступления символоввремя TIME. Поступления единственного символа достаточно, чтобы удовлетворитьзапрос на чтение, и завершить read. При выходе read возвращает столько символов,сколько было доступно в очереди ввода, но не более числа байт, указанного аргументомread. Если за время TIME в очереди ввода не появилось символов, read возвращает0.• TIME равно 0, но MIN не равно 0. В этом случае read ждёт, пока в очереди ввода непоявится как минимум MIN байтов. После этого read возвращает столько символов,сколько доступно в очереди, но не более числа байт, указанного аргументом read.
Таким образом, read может вернуть больше, чем MIN символов, если столько символовоказалось в очереди ввода терминала.Например, если параметр MIN установлен в 50, а системный вызов read запрашиваетчтение 10 байт, read будет ждать, пока в очереди ввода не появится 50 байт. Затем readвернёт первые 10 байт, оставляя оставшиеся 40 в очереди ввода для последующих вызововread.1.4 Установка режимов терминалаЧтобы установить некоторый режим работы терминала, прежде всего нужно вызватьtcgetattr, чтобы получить текущий режим работы этого терминала. Затем нужно изменить только те поля, которые устанавливают нужный режим. После этого нужно установитьрежим работы терминала вызовом tcsetattr.Не рекомендуется просто инициализировать структуру struct termios нужным набором значений и передать её непосредственно tcsetattr.
Разные операционные системы могут иметь дополнительные поля, которые здесь не задокументированы, поэтому единственный способ, как можно избежать установки этих полей в неправильные значения, — немодифицировать их.Для правильной работы различные терминалы могут требовать различных установок режимов работы терминала, поэтому не рекомендуется копировать атрибуты с одного терминала на другой терминал.8Когда поле структуры является набором независимых флагов, как поля c_iflag,c_oflag, c_cflag и c_lflag, не рекомендуется устанавливать значение поля целиком,поскольку у каждой операционной системы могут быть дополнительные флаги.
Вместо этогонужно модифицировать только те биты флагов, которые необходимы программе, оставляядругие в неприкосновенности.1.5 Пример неканонического вводаСледующая программа переводит терминал в неканонический режим, отключает эхо ивводит символы до тех пор, пока не будет введён символ с кодом 4 (Ctrl-D).#include#include#include#include#include<unistd.h><stdio.h><stdlib.h><termios.h><signal.h>/* переменная для сохранения исходных атрибутов терминала */struct termios saved_attributes;void reset_input_mode(void){tcsetattr(0, TCSANOW, &saved_attributes);}void sighnd(int signo){exit(0);}void set_input_mode(void){struct termios tattr;/* проверяем, что вводим с терминала */if (!isatty(0)) {fprintf(stderr, "Not a terminal.\n");exit(1);}/* считываем текущий режим работы терминала */tcgetattr(0, &saved_attributes);memcpy(&tattr, &saved_attributes, sizeof(tattr));/* включаем неканонический режим без эха */tattr.c_lflag &= ~(ICANON|ECHO);/* считываем минимум по одному символу */tattr.c_cc[VMIN] = 1;/* без ограничения времени ожидания */tattr.c_cc[VTIME] = 0;/* устанавливаем новый режим работы терминала */tcsetattr(0, TCSAFLUSH, &tattr);}int main(void){9char c;set_input_mode();atexit(reset_input_mode);/* устанавливаем обработку сигналов завершения */signal(SIGINT, sighnd);signal(SIGTERM, sighnd);while (1) {read(0, &c, 1);if (c == ’\004’) break;write(1, &c, 1);}return 0;}1.6 Вывод на терминалСам по себе вывод на терминал не имеет никаких особенностей по сравнению, например, с выводом в файл.
Можно использовать системный вызов write, можно использоватьфункции высокоуровневого вывода fprintf и т. д. При использовании высокоуровневыхфункции вывода может потребоваться отключить буферизацию потока вывода, чтобы данные передавались на терминал сразу, а не накапливались во внутреннем буфере потока.
Дляэтого можно использовать вызов setbuf(f, NULL);, где f — имя высокоуровневого потока, связанного с терминалом (обычно это stdout). Если буферизация вывода отключена,можно безопасно перемешивать использование низкоуровневой функции write и высокоуровневых функций работы с потоком.Поскольку, как было сказано выше, существует большое количество разных моделейтерминалов, основная проблема состоит в том, как сделать программу не привязанной кнекоторой конкретной модели терминала. Об этом пойдет речь в следующем разделе.1.7 Система команд терминалаРазные модели терминалов могут иметь разный набор поддерживаемых режимов вывода символов.
Например, матричные принтеры могут поддерживать несколько шрифтов,верхние и нижние индексы и другие возможности. Стандартом де-факто для системы команд матричных принтеров является система команд Epson, но каждая конкретная модельпринтера вносит какие-то добавления в эти команды.
Точно также, стандартом де-факто дляалфавитно-цифровых терминалов с прямой адресацией экрана (то есть, терминалов, позволяющих выводить символ в произвольное место экрана) является система команд VT100, нокаждая конкретная модель терминала добавляет какие-то новые команды.Если программа будет работать только с одним каким-то видом терминала, такая программа не сможет получить широкого распространения. Более того, при разработке программы просто невозможно предусмотреть модели терминалов, которые могут появитьсяуже после того, как программа будет распространена.В системах Unix эта проблема решается следующим образом.
Вместе с системой поставляется файл или набор файлов, содержащий описание возможностей (terminal capability)10всех «известных науке» терминалов. В ранних системах BSD файл описания терминалов назывался /etc/termcap, сейчас повсеместно используется другая форма описания, когда свойства терминала описываются для каждого терминала в отдельном файле, и все эти файлы находятся в подкаталогах каталога /usr/share/terminfo или/usr/lib/terminfo. Описание терминала содержит сведения о том, допускает ли терминал прямую адресацию, и какая последовательность символов для этого должна быть передана, сколько у терминала стобцов и строк и пр.Алфавитно-цифровые терминалы, как правило, работают в побайтовом режиме, то естькаждый байт — это символ, который нужно вывести на текущую позицию экрана, либо некоторая команда управления терминалом.
Кроме того терминал может обрабатывать многобайтные последовательности. Такие последовательности всегда начинаются с кода ’\033’(ESC). Например, для терминала VT100 последовательность ’\033’, ’[’, ’2’, ’J’ очищает экран.Каждый символ, набираемый на клавиатуре, как правило, имеет однобайтный код, соответствующий его коду ASCII. Но некоторые клавиши, такие как клавиши перемещения курсора и некоторые другие, посылают многобайтную последовательность, которая тоже начинается с кода ’\033’ (ESC).
Для того же терминала VT100 клавиша «стрелка вверх»посылает последовательность байтов ’\033’, ’[’, ’A’.1.8 Работа с библиотекой ncursesФункции работы с терминалом находятся в библиотеке curses. Эта библиотека предоставляет возможности для управления терминалом как для вывода символов на экран, так идля чтения кодов символов с клавиатуры.
В современных системах (Linux, FreeBSD) эта библиотека называется ncurses. Чтобы использовать эту библиотеку в программе, она должнаподключать заголовочный файл <ncurses.h>, а при компоновке должна быть указана опция -lncurses.Основным понятием библиотеки является окно, представляющее собой прямоугольнуюобласть на экране. При запуске программы создаётся корневое окно (root window), занимающее весь экран. Пользователь может создавать новые окна меньшего размера. К корневому окну можно обращаться по имени stdscr. При выводе в окно вывод отсекается поправой и по нижней границе окна, то есть, если выводится строка длины большей, чем ширина окна, строка будет обрезана по ширине окна.Все функции, работающие с окнами, не выводят непосредственно на терминал, а модифицируют буфер окна, находящийся в памяти.