Основы программирования в Linux - Нейл Мэтью
Шрифт:
Интервал:
Закладка:
int main() {
int choice = 0;
FILE *input;
FILE *output;
struct termios initial_settengs, new_settings;
3. Перед вызовом функции getchoice вам следует изменить характеристики терминала, этим определяется место следующих строк:
if (!isatty(fileno(stdout))) {
fprintf(stderr, "You are not a terminal, OK.n");
}
input = fopen("/dev/tty", "r");
output = fopen("/dev/tty", "w");
if (!input || !output) {
fprintf(stderr, "Unable to open /dev/ttyn");
exit(1);
}
tcgetattr(fileno(input), &initial_settings);
new_settings = initial_settings;
new_settings.c_lfag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
new_settings.c_lflag &= ~ISIG;
if (tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
fprintf(stderr, "could not set attributesn");
}
4. Перед завершением вы также должны вернуть первоначальные значения:
do {
choice = getchoice("Please select an action", menu, input, output);
printf("You have chosen: %cn", choice);
} while (choice != 'q');
tcsetattr(fileno(input), TCSANOW, &initial_settings);
exit(0);
}
5. Теперь, когда вы в неканоническом режиме, необходимо проверить на соответствие возвраты каретки, поскольку стандартное преобразование CR (возврат каретки) в LF (переход на новую строку) больше не выполняется:
int getchoice (char *greet, char *choices[], FILE *in, FILE *out) {
int chosen = 0;
int selected;
char **option;
do {
fprintf(out, "Choice: %sn", greet);
option = choices;
while (*option) {
fprintf(but, "%sn", *option);
option++;
}
do {
selected = fgetc(in);
} while (selected == 'n' || selected == 'r');
option = choices;
while (*option) {
if (selected == *option[0]) {
chosen = 1;
break;
}
option++;
}
if (!chosen) {
fprintf(out, "Incorrect choice, select againn");
}
} while(!chosen);
return selected;
}
Пока вы не устроите все иначе, теперь, если пользователь нажмет в вашей программе комбинацию клавиш <Ctrl>+<C>, программа завершится. Вы можете отключить обработку этих специальных символов, очистив флаг ISIG в локальных режимах. Для этого в функцию main включается следующая строка:
new_settings.c_lflag &= ~ISIG;
Если вы внесете эти изменения в вашу программу меню, то будете получать немедленный отклик, и вводимый вами символ не будет отображаться на экране.
$ ./menu4
Choice: Please select an action
a — add new record
d — delete record
q — quit
You have chosen: a
Choice: Please select an action
a — add new record
d — delete record
q — quit
You have chosen: q $
Если вы нажмете комбинацию клавиш <Ctrl>+<C>, символ будет передан прямо в программу и будет истолкован, как неверный выбор.
Вывод терминала
С помощью структуры типа termios вы управляли вводом с клавиатуры, но было бы хорошо иметь такой же уровень управления выходными данными, отображаемыми на экране терминала. В начале главы вы применяли функцию printf для вывода символов на экран, не имея при этом возможности помещать их в определенное место экрана.
Тип терминала
Во многих системах UNIX применяются терминалы, несмотря на то, что сегодня во многих случаях "терминал" может на самом деле быть ПК, выполняющим программу эмуляции терминала или терминальным приложением в оконной среде, таким как xterm в графической оболочке X11.
Исторически существовало очень большое число аппаратных терминалов разных производителей. Несмотря на то, что почти все они применяют escape-последовательности (строки символов, начинающиеся с escape-символа) для управления положением курсора и другими атрибутами, такими как жирное начертание или мерцание, способы реализации управления при этом слабо стандартизованы. У некоторых старых моделей терминалов также разные характеристики прокрутки экрана, который может очищаться или не очищаться, когда посылается символ Backspace, и т.д.
ПримечаниеСуществует стандарт ANSI для набора escape-последовательностей (в основном базирующихся на последовательностях, применяемых в серии VT-терминалов компании Digital Equipment Corporation, но не идентичных им). Многие терминальные программы обеспечивают эмуляцию стандартного аппаратного терминала, часто VT100, VT220 или ANSI, а иногда и других типов.
Такое разнообразие аппаратных моделей терминалов было бы огромной проблемой для программистов, пытающихся написать программы управления экраном, выполняющиеся на терминалах разных типов. Например, терминал ANSI применяет последовательность символов Escape, [, A для перемещения курсора вверх на одну строку. Терминал ADM-За (очень распространенный несколько лет назад) использует один управляющий символ от комбинации клавиш <Ctrl>+<K>.
Написание программы, имеющей дело с терминалами разнообразных типов, которые могут быть подключены в системе UNIX, кажется крайне устрашающей задачей. Такой программе понадобится разный программный код для терминала каждого типа.
Как ни странно, решение существует в пакете, известном как terminfo. Вместо необходимости обслуживания любого типа терминала в каждой программе, ей достаточно просмотреть базу данных типов терминалов для получения корректной информации. В большинстве современных систем UNIX, включая Linux, эта база данных объединена с другим пакетом, названным curses, о котором вы узнаете в следующей главе.
Для применения функций terminfo вы, как правило, должны подключить заголовочный файл curses.h пакета curses и собственный заголовочный файл term.h пакета terminfo. В некоторых системах Linux вам, возможно, придется применять реализацию curses, известную как ncurses, и включить файл ncurses.h для предоставления прототипов вашим функциям terminfo.
Установите тип вашего терминала
Окружение ОС Linux содержит переменную TERM, которая хранит тип используемого терминала. Обычно она устанавливается системой автоматически во время регистрации в системе. Системный администратор может задать тип терминала по умолчанию для каждого непосредственно подключенного терминала и может сформировать подсказку с типом терминала для удаленных сетевых пользователей. Значение TERM может быть передано rlogin через telnet.
Пользователь может запросить командную оболочку о соображениях системы по поводу используемого им или ею терминала:
$ echo $TERM
xterm
$
В данном случае оболочка выполняется из программы, называемой xterm — эмулятора терминала для графической оболочки X Window System, или программы, обеспечивающей "такие же функциональные возможности, как KDE's Konsole или GNOME's gnome-terminal.
Пакет terminfo содержит базу данных характеристик и управляющих escape-последовательностей для большого числа терминалов и предоставляет единообразный программный интерфейс для их использования. Отдельная программа, таким образом, сможет извлечь выгоду от применения новых моделей терминалов по мере расширения базы данных и не заботиться о поддержке множества разных терминалов.
Характеристики терминалов в terminfo описываются с помощью атрибутов. Они хранятся в наборе откомпилированных файлов terminfo, которые обычно находятся в каталогах /usr/lib/terminfo или /usr/share/terminfo. Для каждого терминала (и многих принтеров, которые тоже могут быть заданы в terminfo) есть файл, в котором определены характеристики терминала и способ доступа к его функциям. Для того чтобы не создавать слишком большого каталога, реальные файлы хранятся в подкаталогах, имена которых — первый символ типа терминала. Так определение терминала VT100 можно найти в файле …terminfo/v/vt100.
Файлы terminfo пишутся по одному на каждый тип терминала в исходном формате, пригодном (или почти пригодном!) для чтения, который затем компилируется командой tic в более компактный и эффективный формат, используемый прикладными программами. Странно, стандарт X/Open ссылается на описания исходного и откомпилированного формата, но не упоминает команду tic, необходимую для реального преобразования исходного формата в откомпилированный. Для вывода пригодной для чтения версии откомпилированного элемента набора terminfo можно использовать программу infocmp.
Далее приведен пример файла terminfo для терминала VT100:
$ infocmp vt100
vt100|vt100-am|dec vt100 (w/advanced video),