КаталогИндекс раздела
НазадОглавлениеВперед


Управление терминалами (curses/terminfo)

СОДЕРЖАНИЕ

Предисловие

1. Введение
    1.1. Что такое curses?
    1.2. Что такое terminfo?
    1.3. Взаимодействие curses и terminfo
    1.4. Другие компоненты пакета управления терминалом

2. Использование подпрограмм пакета curses
    2.1. Что нужно программе для работы с curses
        2.1.1. Файл <curses.h>
        2.1.2. Подпрограммы initscr(), refresh() и endwin()
    2.2. Компиляция программы, которая использует curses
    2.3. Выполнение программы, которая использует curses
    2.4. Еще о строках, столбцах и подпрограмме initscr()
    2.5. Еще об окнах и подпрограмме refresh()
    2.6. Несложный ввод/вывод
        2.6.1. Вывод
        2.6.2. Ввод
    2.7. Управление вводом/выводом
        2.7.1. Атрибуты вывода
        2.7.2. Звонки, свист, вспышки
        2.7.3. Опции ввода
    2.8. Работа с окнами
        2.8.1. Ввод/вывод
        2.8.2. Подпрограммы wnoutrefresh() и doupdate()
        2.8.3. Новые окна
    2.9. Прочие возможности пакета curses
        2.9.1. Линии на экране и прочая графика
        2.9.2. Использование программируемых меток
        2.9.3. Работа с несколькими терминалами сразу

3. Использование подпрограмм пакета terminfo
    3.1. Что нужно программе для работы с terminfo
    3.2. Компиляция и выполнение программ, которые используют terminfo
    3.3. Пример программы, работающей с terminfo

4. Использование базы данных terminfo
    4.1. Создание описания терминала
        4.1.1. Название терминала
        4.1.2. Выяснение характеристик терминала
        4.1.3. Указание характеристик терминала
          4.1.3.1. Основные характеристики
          4.1.3.2. Характеристики экрана
          4.1.3.3. Характеристики клавиатуры
          4.1.3.4. Параметризованные цепочки
        4.1.4. Компиляция описания
        4.1.5. Тестирование описания
    4.2. Печать и сравнение описаний в базе данных terminfo
    4.3. Преобразование termcap-описания в terminfo-описание

5. Примеры программ, работающих с curses
    5.1. Программа editor
    5.2. Программа highlight
    5.3. Программа scatter
    5.4. Программа show
    5.5. Программа two
    5.6. Программа window


 

ПРЕДИСЛОВИЕ

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

В этой главе объясняется, как разрабатывать программы, работающие с терминалом в системе UNIX, используя пакет подпрограмм, называемый cursescurses/terminfo . Этот пакет включает библиотеку подпрограмм на языке C, базу данных и набор вспомогательных средств системы UNIX. Назначение данной главы - не описание всех компонент пакета, а предоставление пользователю возможности немедленно приступить к написанию программ, работающих с терминалом. Здесь рассматриваются только наиболее часто используемые подпрограммы, в отношении же остальных делается ссылка на curses(3X) и terminfo(4) в Справочнике программиста. Держите эту книгу под рукой на случай, если Вам нужно будет узнать подробности о какой-либо из описанных (или не описанных) здесь подпрограмм.

Для использования cursescurses/terminfo нужно быть знакомым с языком программирования C, так как подпрограммы пакета написаны именно на нем. Кроме того, необходимо знать стандартный пакет средств ввода/вывода системы UNIX для языка C [см. stdio(3S)]. Вы сможете создавать разнообразные программы для работы с терминалом, пользуясь этими знаниями и пониманием принятого в системе UNIX принципа опоры на работу, уже сделанную другими.

Данная глава делится на пять разделов:

  1. Введение
    В этом разделе кратко описываются curses, terminfo и другие компоненты пакета подпрограмм для работы с терминалом.
  2. Использование подпрограмм пакета curses
    В этом разделе описываются основные подпрограммы, составляющие библиотеку curses(3X). Рассказывается о подпрограммах вывода на экран, чтения с экрана, построения окон. Описываются также подпрограммы, реализующие более сложные функции - линейную графику, использование терминальных программируемых меток, работу с несколькими терминалами одновременно. Приводится множество примеров, демонстрирующих эффективность использования этих подпрограмм.
  3. Использование подпрограмм пакета terminfo
    В этом разделе описываются те подпрограммы пакета curses, которые непосредственно взаимодействуют с базой данных terminfo для реализации таких возможностей, как программируемые функциональные клавиши.
  4. Использование базы данных terminfo
    В этом разделе описывается база данных terminfo, соответствующие средства поддержки и их взаимодействие с библиотекой curses.
  5. Примеры программ, работающих с curses
    В этом разделе приведены тексты шести программ, иллюстрирующие использование подпрограмм пакета curses.

 

1. ВВЕДЕНИЕ

1.1. Что такое curses?

Curses(3X) - это библиотека подпрограмм, которые используются для разработки программ, осуществляющих ввод/вывод на экран терминала в системе UNIX. Эти подпрограммы являются функциями C или макросами. Многие из них напоминают подпрограммы из стандартной библиотеки языка C. Например, имеется подпрограмма printw(), весьма похожая на printf(3S) и подпрограмма getch(), подобная getc(3S). В Вашем банке программа - автоматический кассир может использовать printw() для вывода меню и getch() для приема Ваших запросов на изъятие сумм (или, что даже лучше, на их вклад). Экранный текстовый редактор - такой, например, как редактор vi(1) системы UNIX, также может использовать эти и другие подпрограммы пакета curses.

Название curses принято из-за того, что данная библиотека подпрограмм оптимизирует движение курсора, то есть минимизирует это движение в процессе обновления экрана. Например, если (используя подпрограммы пакета curses) Вы разработали текстовый редактор и редактируете фразу

       curses/terminfo - отличный пакет для работы с экраном

так, чтобы она читалась:

       curses/terminfo -   лучший пакет для работы с экраном

то программа выведет только 'лучший' вместо 'отличный', остальные символы останутся без изменений. Оптимизация управления курсором называется еще оптимизацией вывода, поскольку минимизируется объем передаваемых данных, то есть вывод.

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

Чем будет полезна оптимизация управления курсором Вам и тем, кто будет пользоваться Вашими программами? Во-первых, она сэкономит Ваше время, затрачиваемое на описание того, как именно Вы хотите изменять содержимое экрана. Во-вторых, она сохранит время пользователя за счет уменьшения времени, необходимого для переписывания экрана. В-третьих, она уменьшит загрузку линий связи системы UNIX в период обновления экрана. В-четвертых, Вам не придется задумываться об огромном количестве терминалов, на которых Ваша программа, быть может, будет работать.

Далее приводится текст простой программы, работающей с curses. Она обращается к нескольким подпрограммам curses для того, чтобы передвинуть курсор на середину экрана и вывести цепочку символов BullsEye. Все эти подпрограммы описываются в следующем разделе, который называется Использование подпрограмм пакета curses. Чтобы понять, что делают эти подпрограммы, Вам достаточно взглянуть на их имена.

       #include <curses.h>
       
       main ()
       {
         initscr ();
         move (LINES/2 - 1, COLS/2 - 4);
         addstr ("Bulls");
         refresh ();
         addstr ("Eye");
         refresh ();
         endwin ();
       }

1.2. Что такое terminfo?

Под terminfo мы понимаем следующее:

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

       # Очистить экран и показать позицию 0,0
       #
       tput clear
       tput cup 0 0     # или tput home
       echo "<- это позиция 0 0"
       #
       # Показать позицию 5,10
       #
       tput cup 5 10
       echo "<- это позиция 5 10"

1.3. Взаимодействие curses и terminfo

Подпрограммы пакета curses извлекают из базы данных terminfo информацию о том типе терминала, на котором программа выполняется. В дальнейшем этот терминал мы будем называть текущим терминалом.

Предположим, что программа, текст которой приведен в конце раздела Что такое curses?, выполняется на терминале AT&T Teletype 5425. Чтобы отработать должным образом, то есть вывести BullsEye в центр экрана, программа должна иметь информацию о количестве строк и столбцов на экране. Эти данные хранятся в описании терминала AT&T Teletype 5425 в базе данных terminfo. Чтобы получить их, программе, работающей с curses, достаточно знать только название терминала, на котором она выполняется. Можно передать ей это название, поместив его в переменную окружения $TERM при подключении к системе UNIX, или путем присвоения значения и помещения в окружение переменной $TERM в Вашем файле .profile [см. profile(4)]. Располагая значением $TERM, программа, использующая curses, может извлечь описание текущего терминала из базы данных terminfo.

Пусть, например, в .profile включены следующие строки:

       TERM=5425
       export TERM
       tput init

В первой строке устанавливается название терминала, а во второй оно помещается в окружение. Третья строка примера требует от системы UNIX проинициализировать текущий терминал, то есть обеспечить соответствие состояния терминала его описанию в базе данных terminfo. (Порядок этих строк важен. Чтобы при вызове tput произошла правильная инициализация текущего терминала, $TERM должна быть уже установлена и помещена в окружение). Если, имея такие строки в файле .profile, Вы запустите программу, работающую с curses, она получит нужную ей информацию о терминале из файла, соответствующего значению $TERM, то есть из файла /usr/lib/terminfo/a/att5425.

1.4. Другие компоненты пакета управления терминалом

Как было сказано выше, пакет управления терминалом обычно называют curses/terminfo. Однако, в нем есть и другие компоненты, из которых мы уже упомянули, в частности, tic(1M). Далее приводится полный список компонент, рассматриваемых в этом руководстве:

 captoinfo(1M)  Средство для перевода описаний терминалов, созданных под ранними версиями системы UNIX, в формат terminfo.
 curses(3X)
 infocmp(1M) 
Средство для печати и сравнения скомпилированных описаний терминалов.
 tabs(1)  Средство для установки нестандартных позиций табуляции.
 terminfo(4)
 tic(1M) 
Компилятор описаний терминалов для базы данных terminfo.
 tput(1)  Средство для установки позиций табуляции на терминале и получения значений его характеристик.

См. также profile(4), scr_dump(4), term(4) и term(5). Более подробную информацию об этих компонентах см. в Справочнике программиста и Справочнике пользователя.

 

2. ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММ ПАКЕТА CURSES

В этом разделе рассматриваются основные подпрограммы пакета curses, которые используются при создании интерактивных терминальных программ. В начале раздела описаны подпрограммы (и другие компоненты), которые нужны для нормальной работы любой программе, работающей с пакетом (в дальнейшем мы для краткости будем иногда называть такие программы curses-программами). В оставшейся части раздела рассказывается о том, как компилировать и выполнять такую программу. Наконец, описываются наиболее часто применяемые подпрограммы, которые делают следующее:

По мере описания подпрограмм мы приводим простые программы, иллюстрирующие их применение. Кроме того, мы ссылаемся на группу более значительных примеров, находящихся в разделе Примеры программ, работающих с curses. Эти последние программы более глубоки и зачастую используют подпрограммы, которые здесь не обсуждаются, поэтому держите под рукой справочник curses(3X).

2.1. Что нужно программе для работы с curses

Если программа использует пакет curses, она должна включать файл <curses.h> и вызывать подпрограммы initscr(), refresh(), или им подобные, а также endwin().

2.1.1. Файл <curses.h>

В файле <curses.h> определяются несколько глобальных переменных и структур данных, а также те из подпрограмм пакета, которые в действительности являются макросами.

Начнем с рассмотрения определяемых в файле переменных и структур данных. В файле <curses.h> определены параметры всех подпрограмм, входящих в пакет curses. Кроме того, определяются целочисленные переменные LINES и COLS, которым при выполнении программы назначаются значения соответственно вертикального и горизонтального размера экрана. Это делается при вызове описываемой ниже подпрограммы initscr(). В файле определяются также константы OK и ERR. Большинство подпрограмм curses возвращают OK при нормальном завершении и ERR при возникновении ошибки.

Примечание

LINES и COLS являются внешними (глобальными) переменными, представляющими размеры экрана. В пользовательском окружении можно установить значения двух подобных им переменных, $LINES и $COLUMNS. Программы curses используют значения этих переменных окружения для определения размера экрана. В этой главе при ссылках на переменные окружения используется знак $, чтобы отличить их от переменных, объявляемых в файле <curses.h>. Дополнительную информацию об этих переменных см. ниже в разделах Подпрограммы initscr(), refresh() и endwin() и Еще о строках, столбцах и подпрограмме initscr().

Рассмотрим теперь макроопределения. Многие подпрограммы curses определены в <curses.h> как макросы, которые обращаются к другим подпрограммам и макросам из curses. Например, refresh() является макросом. Определение

       #define refresh() wrefresh(stdscr)

показывает, что вызов refresh расширяется в обращение к также входящей в curses подпрограмме wrefresh(). Эта последняя, в свою очередь, вызывает две другие подпрограммы curses: wnoutrefresh() и doupdate(). Многие подпрограммы обеспечивают нужный результат, группируя два-три обращения к другим.

Предостережение

Макрорасширения curses могут создать проблемы при использовании некоторых конструкций языка C, например, ++ или --.

Одно последнее замечание о <curses.h>: он автоматически включает <stdio.h>, файл интерфейса с драйвером tty, <termio.h>. Повторное включение в программу этих файлов безвредно, но и бессмысленно.

2.1.2. Подпрограммы initscr( ), refresh( ) и endwin( )

Подпрограммы initscr(), refresh() и endwin() приводят терминал в состояние "работа с curses", обновляют содержимое экрана и восстанавливают терминал в состоянии "вне curses" соответственно. Для уяснения действия каждой из этих подпрограмм воспользуемся еще раз нашим простым примером.

       #include <curses.h>
       
       main ()
       {
         initscr ();     /* Инициализируем терминал и
                            переменные и структуры данных
                            из <curses.h> */
         move (LINES/2 - 1, COLS/2 - 4);
         addstr ("Bulls");
         refresh ();     /* Выводим данные на экран
                            терминала */
         addstr ("Eye");
         refresh ();     /* Выводим еще на экран
                            терминала */
         endwin ();      /* Восстанавливаем состояние
                            терминала */
       }

Curses-программа начинается обычно с вызова initscr(); это достаточно сделать один раз. Подпрограмма initscr() определяет тип терминала, на котором выполняется программа, по значению переменной окружения $TERM, как это описано в разделе Взаимодействие curses и terminfo. Затем подпрограмма инициализирует все переменные и структуры данных, описанные в файле <curses.h>. initscr() присваивает переменным LINES и COLS значения, соответствующие терминалу, на котором выполняется программа. Например, если используется Teletype 5425, она присвоит значение 24 переменной LINES, а значение COLS станет равным 80. Если возникает ошибка, подпрограмма записывает сообщение об этом в stderr и завершает работу.

Ввод/вывод при выполнении программы производится подпрограммами, подобными использующимся в примере move() и addstr(). Например,

       move (LINES/2 - 1, COLS/2 - 4);

требует перемещения курсора в точку, находящуюся несколько левее центра экрана. Затем

       addstr ("Bulls");
требует вывести цепочку символов Bulls. Если программа выполняется на терминале Teletype 5425, эта цепочка будет выведена, начиная с позиции (11, 36).

Внимание!

Все подпрограммы curses, перемещающие курсор, отсчитывают его координаты относительно левого верхнего угла экрана. Координаты (LINES, COLS) этого угла принимаются за (0, 0), а не за (1, 1). Заметьте, что первой идет вертикальная координата, а затем горизонтальная, в отличие от обычной записи 'x, y' для экранных (или графических) координат. Для перемещения курсора в среднюю строку экрана в примере появляется -1, чтобы учесть координаты (0, 0) для угловой позиции.

При вызове подпрограмм типа move() и addstr() содержимое физического экрана терминала не меняется. Экран обновляется только при вызове refresh(). До такого вызова изменяется только внутреннее представление экрана, которое называется окном. Эту весьма важную концепцию мы обсудим далее в разделе Еще об окнах и подпрограмме refresh().

В заключение отметим, что программа, работающая с curses, заканчивается вызовом endwin(). Эта подпрограмма восстанавливает прежнее состояние терминала и помещает курсор в левый нижний угол экрана.

2.2. Компиляция программы, которая использует curses

Компилируйте такую программу, как обычную программу на языке C, командой cc(1) (см. Справочник программиста), которая вызывает компилятор языка C.

Подпрограммы обычно хранятся в библиотеке /usr/lib/libcurses.a. Чтобы редактор внешних связей просматривал эту библиотеку, в команде cc необходимо указать опцию -l.

Общий формат командной строки для компиляции программы, работающей с curses, таков:

       cc файл.c -lcurses -o файл

Файл.c - это имя файла с исходным текстом программы; файл - имя выполнимого объектного файла.

2.3. Выполнение программы, которая использует curses

В процессе своей работы подпрограммы curses опираются на информацию из окружения пользователя. В частности, пользователю такой программы обычно нужно включить в свой файл .profile следующие три строки:

       TERM=тип_текущего_терминала
       export TERM
       tput init

Смысл этих строк объясняется в разделе Взаимодействие curses и terminfo. Пользователи curses-программы могут, кроме того, установить в файле .profile значения переменных окружения $LINES, $COLUMNS и $TERMINFO, однако, в отличие от случая с $TERM, делать это не обязательно.

Если Ваша curses-программа не работает должным образом, Вы, возможно, попытаетесь ее отладить с помощью sdb(1), как это описывается в Справочнике программиста. Пользуясь sdb, нужно учитывать ряд обстоятельств. Во-первых, curses-программа интерактивна и контролирует положение курсора. Однако, интерактивный отладчик, подобный sdb, может произвести на экране изменения, о которых не будет знать отлаживаемая программа.

Во-вторых, программа, работающая с curses, выводит в окно до тех пор, пока не будет вызвана refresh() или подобная ей подпрограмма. В связи с такой задержкой вывода на экран, отладка его правильности может быть затруднена.

В-третьих, во входящих в curses макросах нельзя установить точки прерывания. Вместо макросов нужно использовать подпрограммы, к которым эти макросы обращаются - например, к wrefresh() вместо refresh(), Более подробную информацию о макросах см. выше в разделе Файл <curses.h>.

2.4. Еще о строках, столбцах и подпрограмме initscr( )

Определив размеры экрана терминала, initscr() присваивает значения переменным LINES и COLS. Эти значения берутся из переменных terminfo, называемых lines и columns. Последние, в свою очередь, извлекаются из базы данных terminfo, если не установлены значения переменных окружения $LINES и $COLUMNS.

2.5. Еще об окнах и подпрограмме refresh( )

Как указывалось ранее, подпрограммы curses не обновляют экран, пока не будет вызвана refresh(). Когда refresh() вызывается, все данные, накопленные для вывода, пересылаются на экран текущего терминала.

Эффект использования окна во многом подобен эффекту использования буфера при работе с редактором системы UNIX. Например, когда Вы редактируете файл при помощи vi(1), все изменения содержимого файла отражаются в буфере, а сам файл изменяется только после выдачи команд w или zz. Аналогично, когда Вы вызываете программу, работающую с экраном через пакет curses, изменяется содержимое окна, а сам экран терминала перерисовывается только при вызове refresh(). В <curses.h> содержится описание принятого по умолчанию окна stdscr (стандартное окно), размеры которого совпадают с размерами экрана терминала. В <curses.h> stdscr имеет тип WINDOW*, указатель на структуру языка C, которую пользователь может представлять себе в виде двумерного массива символов, соответствующего экрану терминала. Программа всегда отслеживает как состояние stdscr, так и состояние физического экрана. refresh(), когда вызывается, сравнивает их и посылает на терминал последовательность символов, приводящий его экран в соответствующий содержимому stdscr вид. При этом выбирается один из многих способов сделать это, с учетом характеристик терминала и возможного сходства того, что есть на экране и того, что содержится в окне. Выходной поток оптимизируется таким образом, чтобы он содержал как можно меньше символов. На рисунке ниже показано, что происходит при работе программы, выводящей BullsEye в центре экрана (см. раздел Что такое curses?). Обратите внимание, что, какой бы мусор ни был на экране, он там и останется, пока не будет вызвана refresh(). При этом вызове экран очищается и заполняется текущим содержимым stdscr.

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

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

Некоторые подпрограммы пакета curses предназначены для работы с окнами особого типа, которые мы будем называть спецокнами. Спецокно - это такое окно, размер которого не ограничивается размером экрана и которое не связано с каким-либо определенным местом на экране. Их можно применять, если Вам нужны очень большие окна, или же такие, которые нужно отображать на экран частями. Спецокна могут, например, понадобиться при работе с электронными таблицами.

Ниже показаны взаимосвязи между несколькими окнами, подокнами и спецокнами и экраном терминала.

В разделе Работа с окнами описываются подпрограммы, необходимые для создания и использования окон. Если Вы хотите сейчас увидеть программу, работающую с окнами средствами curses, см. программу window в разделе Примеры программ, работающих с curses.

2.6. Несложный ввод/вывод

2.6.1. Вывод

curses содержит подпрограммы для записи в stdscr, похожие на подпрограммы из stdio(3S) для записи в файл. Они позволяют:

Далее следуют описания и примеры применения этих подпрограмм.

Предостережение

Библиотека curses располагает своим собственным набором подпрограмм ввода/вывода. Если Вы работаете с curses, Вам не следует использовать для ввода/вывода других подпрограмм или обращений к системе, например, read(2) или write(2); в противном случае при выполнении подпрограммы могут возникнуть нежелательные эффекты.

 

addch( )

СИНТАКСИС
 
#include <curses.h>
       
int addch (ch)
chtype ch;
ОПИСАНИЕ
  addch() записывает один символ в stdscr.

Этот символ имеет тип chtype, который определяется в <curses.h>. chtype содержит данные и атрибуты (об атрибутах см. ниже в разделе Атрибуты вывода).

Работая с переменными этого типа, объявляйте их принадлежащими именно к типу chtype, а не к тому основному типу (скажем, short), через который объявляется chtype в <curses.h>. Это обеспечит совместимость с будущими версиями.

addch() производит некоторую перекодировку. Так, она преобразует:

  • символ перевода строки в последовательность, обеспечивающую очистку до конца строки и переход к новой строке; символ табуляции в соответствующее количество пробелов;
  • другие управляющие символы в их запись в нотации ^X.

addch() обычно возвращает OK, за исключением случая вывода символа в правом нижнем углу нероллируемого окна, когда возвращается ERR.

addch() является макросом.

ПРИМЕР
 
    #include <curses.h>
       
    main ()
    {
      initscr ();
      addch ('a');
      refresh ();
      endwin ();
    }

Эта программа выводит следующее:

См. также программу show в разделе Примеры программ, работающих с curses.

 

addstr( )

СИНТАКСИС
 
#include <curses.h>
       
int addstr (str)
char *str;
ОПИСАНИЕ
  addstr() выводит цепочку символов в stdscr.

addstr() вызывает addch() для вывода каждого символа.

addstr() производит ту же перекодировку, что и addch().

addstr() возвращает OK при успешном завершении и ERR при ошибке.

addstr() является макросом.

ПРИМЕР
  См. примеры программ во Введении.

 

printw( )

СИНТАКСИС
 
#include <curses.h>
       
int printw(fmt [,arg...])
char *fmt;
ОПИСАНИЕ
  printw() осуществляет форматированный вывод в stdscr.

Подобно printf(3S), printw() получает формат и переменное количество аргументов.

Подобно addstr(), printw() обращается к addch() для вывода каждого символа.

printw() возвращает OK при успешном завершении и ERR при ошибке.

ПРИМЕР
 
       #include <curses.h>
       
       main ()
       {
         char *title = "Не указано";
         int no = 0;
               . . .
         initscr ();
               . . .
         printw ("%s отсутствует на складе.", title);
         printw ("Попросите кассира заказать %d для Вас.\n", no);
         refresh ();
         endwin ();
       }

Эта программа выводит следующее:

 

move( )

СИНТАКСИС
 
#include <curses.h>
       
int move (y, x);
int y, x;
ОПИСАНИЕ
  move() устанавливает курсор в позицию "строка y, колонка x" окна stdscr.

Обратите внимание, что первым аргументом move() является координата y, а вторым - x. Координаты левого верхнего угла stdscr равны (0, 0), а правого нижнего - (LINES-1, COLS-1). Подробнее см. в разделе Подпрограммы initscr(), refresh() и endwin().

Перемещение курсора можно производить одновременно с выводом данных, а именно:

  • mvaddch (y, x, ch) перемещает курсор и выводит символ;
  • mvaddstr (y, x, str) перемещает курсор и выводит цепочку символов;
  • mvprintw (y, x, fmt[,arg...]) перемещает курсор и выводит форматированную строку.

move() возвращает OK при нормальном завершении и ERR при ошибке. К ошибке приводит попытка сдвинуть курсор в позицию, не находящуюся между (0, 0) и (LINES-1, COLS-1).

move() является макросом.

ПРИМЕР
 
       #include <curses.h>
       
       main()
       {
         initscr ();
         addstr ("Курсор должен быть здесь -->");
         addstr (" если move () работает.");
         printw ("\n\n\nНажмите <CR> для завершения теста.");
         move (0, 28);
         refresh ();
         getch ();       /* Вводит <CR>, см. ниже */
         endwin ();
       }

Эта программа выводит следующее:

После нажатия возврата каретки экран будет выглядеть так:

Другой пример использования move() можно найти в программе scatter в разделе Примеры программ, работающих с curses.

 

clear( ), erase( )

СИНТАКСИС
 
#include <curses.h>
       
int clear ( )
       
int erase ( )
ОПИСАНИЕ
  Обе подпрограммы заполняют все окно stdscr пробелами.

clear() допускает наличие на экране мусора, о котором она не знает; эта подпрограмма вызывает сначала erase(), а затем clearok(), что приводит к полной очистке физического экрана при следующем вызове refresh() для stdscr. Подробнее о clearok() см. curses(3X).

initscr() автоматически вызывает clear().

clear() всегда возвращает OK, erase() не возвращает ничего полезного.

И clear(), и erase() являются макросами.

 

clrtoeol( ), clrtobot( )

СИНТАКСИС
 
#include <curses.h>
       
int clrtoeol ( )
       
int clrtobot ( )
ОПИСАНИЕ
  clrtoeol() заменяет остаток строки пробелами.

clrtobot() заменяет остаток экрана пробелами.

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

Ни одна из подпрограмм не возвращает ничего полезного.

ПРИМЕР
  Приведем текст программы, использующей clrtobot().
       #include <curses.h>
       
       main ()
       {
         initscr ();
         addstr ("Нажмите <CR> для удаления отсюда ");
         addstr ("до конца строки и далее.");
         addstr ("\nУдалите это тоже.\nИ это.");
         move (0, 32);
         refresh ();
         getch ();
         clrtobot ();
         refresh ();
         endwin ();
       }

Эта программа выводит следующее:

Обратите внимание на два вызова refresh() - первый выводит этот текст на экран, второй очищает экран с отмеченного курсором места до конца.

После нажатия возврата каретки экран будет выглядеть так:

Примеры использования clrtoeol() см. в программах show и two в разделе Примеры программ, работающих с curses.

2.6.2. Ввод

Подпрограммы curses, предназначенные для чтения с текущего терминала, подобны тем, которые содержатся в stdio(3S) и читают из файла. Они позволяют:

Первичной подпрограммой является getch(), которая обрабатывает и возвращает значение одного введенного символа. Она подобна подпрограмме getchar(3S) из библиотеки языка C, за исключением того, что она может производить некоторые зависящие от системы или терминала действия, которые не выполняет getchar(). Например, getch() можно использовать совместно с подпрограммой keypad() из библиотеки curses. Это позволяет программе распознавать и обрабатывать, как один символ, последовательности, начинающиеся с ESC, которые передаются, например, при нажатии клавиш управления курсором или функциональных клавиш. Дополнительную информацию о keypad() см. в описаниях getch() и keypad() в curses(3X).

Ниже описываются основные подпрограммы для ввода с терминала и даются примеры их использования.

 

getch( )

СИНТАКСИС
 
#include <curses.h>
       
int getch ( )
ОПИСАНИЕ
  getch() читает один символ с текущего терминала.

getch() возвращает значение этого символа или ERR при возникновении ситуации "конец файла", получении сигнала, или при чтении без ожидания, если символ еще не введен.

getch() является макросом.

См. далее, а также в curses(3X) описания echo(), noecho(), cbreak(), nocbreak(), raw(), noraw(), halfdelay(), nodelay() и keypad().

ПРИМЕР
 
       #include <curses.h>
       
       main ()
       {
         int ch;
         initscr ();
         cbreak ();    /* Пояснения см. в
                           разделе "Опции ввода" */
         addstr ("Введите любой символ:  ");
         refresh ();
         ch=getch ();
         printw ("\n\n\nВы ввели '%c'.\n", ch);
         refresh ();
         endwin ();
       }

Посмотрим, что выводит эта программа. Первый refresh() выводит цепочку символов, указанную в addstr(), из stdscr на экран терминала:

Пусть на клавиатуре нажали w. getch() принимает символ и его значение присваивается ch. Наконец, второй раз вызывается refresh() и экран становится таким:

Другой пример использования getch() см. в программе show в разделе Примеры программ, работающих с curses.

 

getstr( )

СИНТАКСИС
 
#include <curses.h>
       
int getstr (str)
char *str;
ОПИСАНИЕ
  getstr() читает символы и сохраняет их в буфере до тех пор, пока не будет нажат возврат каретки, перевод строки или клавиша ввода.

getstr() не проверяет буфер на переполнение.

Прочитанные символы пересылаются в цепочку str.

getstr() является макросом и вызывает getch() для чтения каждого символа.

getstr() возвращает ERR только тогда, когда getch() возвращает ERR. В остальных случаях возвращается OK.

См. далее, а также в curses(3X) описания echo(), noecho(), cbreak(), nocbreak(), raw(), noraw(), halfdelay(), nodelay() и keypad().

ПРИМЕР
 
       #include <curses.h>
       
       main ()
       {
         char str [256];
         initscr ();
         cbreak ();   /* Пояснения см. в
                         разделе "Опции ввода" */
         addstr ("Введите строку символов,");
         addstr (" оканчивающуюся <CR>:\n\n");
         refresh ();
         getstr (str);
         printw ("\n\n\nВы ввели \n'%s'\n", str);
         refresh ();
         endwin ();
       }

Допустим, Вы ввели строку "Мне нравится изучать систему UNIX". После нажатия возврата каретки экран будет выглядеть так:

 

scanw( )

СИНТАКСИС
 
#include <curses.h>
       
int scanw (fmt [,arg...])
char *fmt;
ОПИСАНИЕ
  scanw() вызывает getstr() и сканирует введенную строку.

Подобно scanf(3S), scanw() использует формат для преобразования введенной строки и присваивает значения переменному количеству аргументов.

scanw() возвращает те же значения, что и scanf().

Дополнительную информацию см. в описании scanf(3S).

ПРИМЕР
 
       include <curses.h>
       
       main ()
       {
         char string [100];
         float number;
         initscr ();
         cbreak ();  /* Пояснения см.
                        в разделе "Опции ввода" */
         echo ();
         addstr ("Введите число и текст,");
         addstr (" разделенные запятой:");
         refresh ();
         scanw ("%f,%s", &number, string);
         clear ();
         printw ("Вы ввели текст \"%s\" и число %f.",
                  string, number);
         refresh ();
         endwin ();
       }

Обратите внимание на два вызова refresh(). После первого вызова на экране появляется строка, переданная addstr(), после второго - строка, которую возвращает scanw(). Кроме того, обратите внимание на вызов clear(). Допустим, Вы ввели строку 2,twin. После выполнения программы экран терминала будет выглядеть так:

2.7. Управление вводом/выводом

2.7.1. Атрибуты вывода

Рассказывая об addch(), мы упомянули, что эта подпрограмма выводит в stdscr один знак типа chtype. chtype состоит из двух частей: информации о самом символе и информации о наборе атрибутов, связанных с этим символом. Эти атрибуты позволяют отображать символ с повышенной яркостью, подчеркнутым, инвертированным и т.д.

С stdscr всегда связан набор атрибутов, которые автоматически присваиваются каждому выводимому символу. Вы можете изменить текущие значения атрибутов, используя attrset() или другие подпрограммы пакета curses, которые описаны ниже. Приведем здесь список атрибутов и их описания:

Эти атрибуты можно передавать в качестве аргумента подпрограмме attrset() или ей подобным. Им можно передавать комбинации атрибутов, объединенные операцией дизъюнкции (|).

Примечание

Не каждый терминал может отображать любой из перечисленных атрибутов. Если терминал не может реализовать запрошенный атрибут, curses пытается заменить его похожим, а если и это невозможно, то атрибут игнорируется.

Рассмотрим использование одного из этих атрибутов. Следующий фрагмент программы обеспечивает отображение слова с повышенной яркостью:

          . . .
         printw ("Яркое ");
         attrset (A_BOLD);
         printw ("слово");
         attrset (0);
         printw (" бросается в глаза.\n");
          . . .
         refresh ();

Атрибуты можно включать по одному, как в примере: attrset (A_BOLD), или в комбинации. Например, чтобы вывести яркий мерцающий текст, Вы можете использовать attrset (A_BOLD | A_BLINK). Те или иные атрибуты включаются и выключаются подпрограммами attron() и attroff() без какого-либо влияния на остальные атрибуты отображения. attrset (0) отключает все атрибуты.

Заметьте, что в набор атрибутов входит A_STANDOUT, который можно применять для привлечения внимания пользователя. Для физической реализации этого атрибута используется наиболее визуально выразительный способ отображения, возможный на данном терминале. Обычно это повышенная яркость или инверсия. Если нужно просто выделить часть текста, все равно, подсветкой, инверсией или как-либо еще, лучше использовать A_STANDOUT. Для его включения и выключения удобны функции standout() и standend() соответственно. Фактически, standend() отключает все атрибуты. Кроме перечисленных атрибутов, имеется еще две битовые маски, а именно A_CHARTEXT и A_ATTRIBUTES. Их можно использовать для извлечения только символа или только атрибутов из значения, возвращаемого входящей в curses функцией inch(), путем их конъюнкции (операция & языка C) с этим значением. См. описание inch() в curses(3X).

Приведем описание attrset() и других подпрограмм curses, которые используются для управления атрибутами вывода.

 

attron( ), attroff( ), attrset( )

СИНТАКСИС
 
#include <curses.h>
       
int attron (attrs)
chtype attrs;
       
int attrset (attrs)
chtype attrs;
       
int attroff (attrs)
chtype attrs;
ОПИСАНИЕ
  attron() включает запрошенные атрибуты attrs, сохраняя те, которые уже включены. attrs принадлежит к типу chtype, определяемому во включаемом файле <curses.h>.

attrset() включает запрошенные атрибуты attrs вместо тех, которые включены в момент обращения к ней.

attroff() выключает запрошенные атрибуты attrs, если они включены.

Атрибуты могут объединятся при помощи побитной операции ИЛИ (|).

Все подпрограммы возвращают OK.

ПРИМЕР
  См. программу highlight в разделе Примеры программ, работающих с curses.

 

standout( ), standend( )

СИНТАКСИС
 
#include <curses.h>
       
int standout ( )
       
int standend ( )
ОПИСАНИЕ
  standout() включает атрибут A_STANDOUT и эквивалентен вызову attron (A_STANDOUT).

standend() выключает все атрибуты и эквивалентен вызову attrset (0).

Обе подпрограммы всегда возвращают OK.

ПРИМЕР
  См. программу highlight в разделе Примеры программ, работающих с curses.

2.7.2. Звонки, свист, вспышки

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

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

 

beep( ), flash( )

СИНТАКСИС
 
#include <curses.h>
       
int flash ( )
       
int beep ( )
ОПИСАНИЕ
  flash() пытается заставить вспыхнуть экран терминала. Если это невозможно, flash() пытается включить на терминале звуковой сигнал.

beep() пытается включить на терминале звуковой сигнал. Если это невозможно, beep() пытается заставить вспыхнуть экран терминала.

Ни одна из подпрограмм не возвращает какого-либо осмысленного значения.

2.7.3. Опции ввода

Система UNIX производит множество действий с символами, вводимыми пользователем. Среди прочего, система выполняет следующее:

Curses полностью берет на себя управление экраном терминала, поэтому его подпрограммы отключают эхо, производимое системой UNIX, и делают его самостоятельно. Временами бывает необходимо программно отключать стандартную для системы UNIX обработку символов. Для этого предназначены некоторые подпрограммы curses, например, noecho() и cbreak(). Используя такие подпрограммы, можно управлять интерпретацией вводимых символов. В приведенной ниже таблице перечислены основные подпрограммы, предназначенные для управления режимом ввода.

Если программа использует curses и вводит с терминала, она должна установить некоторые опции ввода. Это связано с тем, что при старте программы терминал может оказаться в любом из режимов cbreak(), raw(), nocbreak(), или noraw(). Как показано в таблице, при запуске программы гарантируется только наличие режима echo().

Как правило, в интерактивной терминальной программе используются комбинация режимов noecho() и cbreak(). Пусть, например, нужно отображать вводимые символы не там, где находится курсор, а в нижней строке экрана. Для этого предназначена подпрограмма noecho(). Однако, хотя noecho() и подавляет отображение вводимых символов, символы забоя и уничтожения обрабатываются обычным образом. Для отключения этого режима используется подпрограмма cbreak().

Опции ввода Символы
Интерпретируются Не интерпретируются
Нормальное состояние "вне curses" прерывание, выход, сброс бита четности, <CR> в <NL>, забой, уничтожение, конец файла  
Нормальное состояние "старт curses-программы" эхо (заменяет эхо системы UNIX) все остальное не определено
cbreak () и echo () прерывание, выход, сброс бита четности эхо забой, уничтожение, конец файла
cbreak () и noecho () прерывание, выход, сброс бита четности забой, уничтожение, конец файла, эхо
nocbreak () и noecho () разрыв, выход, сброс бита четности, забой, уничтожение, конец файла эхо
nocbreak () и echo () См. предостережение ниже
nl () <CR> в <NL>  
nonl ()   <CR> в <NL>
raw () [вместо cbreak ()]   разрыв, выход, сброс бита четности

Предостережение

Не применяйте одновременно nocbreak() и noecho(). Если установлены оба режима и Вы вызываете getch(), программа при чтении каждого символа включает, а затем отключает cbreak(). В зависимости от состояния драйвера терминала при вводе символа, это может привести к выводу нежелательных данных на экран.

Помимо подпрограмм, перечисленных в таблице, для управления вводом используются следующие подпрограммы curses: noraw(), halfdelay(), nodelay(). Они обсуждаются в curses(3X).

Далее подробнее описываются подпрограммы noecho(), cbreak() и связанные с ними echo() и nocbreak().

 

echo( ), noecho( )

СИНТАКСИС
 
#include <curses.h>
       
int echo ( )
       
int noecho ( )
ОПИСАНИЕ
  echo() устанавливает режим "эхо" - отображение символов на экране по мере их ввода. При старте программы этот режим установлен.

noecho() отключает режим "эхо".

Ни та, ни другая подпрограмма не возвращают полезных значений.

curses-программы могут работать неправильно, если эхо установлено одновременно с nocbreak(). См. таблицу выше и последующее предостережение. После отключения эха вводимые символы можно отображать посредством addch().

ПРИМЕР
  См. программы editor и show в разделе Примеры программ, работающих с curses.

 

cbreak( ), nocbreak( )

СИНТАКСИС
 
#include <curses.h>
       
int cbreak ( )
       
int nocbreak ( )
ОПИСАНИЕ
  cbreak() включает режим "прерывание при вводе каждого символа". Программа получает каждый символ сразу после его ввода, но символы забоя, уничтожения и конца файла не обрабатываются.

nocbreak() возвращает к обычному режиму "построчный ввод". Обычно этот режим установлен при запуске прог- раммы.

Ни одна из подпрограмм не возвращает полезных значений.

curses-программы могут работать неправильно, если cbreak() включается и отключается в пределах одной программы, либо если используется комбинация nocbreak() и echo().

См. таблицу выше и последующее предостережение.

ПРИМЕР
  См. программы editor и show в разделе Примеры программ, работающих с curses.

2.8. Работа с окнами

Ранее, в разделе Еще об окнах и подпрограмме refresh(), объяснялось, что такое окна и спецокна и для чего они могут Вам понадобиться. В этом разделе описываются подпрограммы curses, предназначенные для создания окон и спецокон и управления ими.

2.8.1. Ввод/вывод

Для ввода и вывода данных в окна и спецокна используются подпрограммы, похожие на те, которые работают с stdscr. Единственная разница состоит в том, что необходимо указать окно, к которому относится операция. Как правило, имена этих подпрограмм получаются путем добавления буквы w в начало названия соответствующей подпрограммы для stdscr, а также имени окна в качестве первого параметра. Например, если нужно вывести символ c в окно mywin, addch ('c') превращается в waddch (mywin,'c'). Далее приводится список работающих с окнами подпрограмм, соответствующих тем, которые были описаны в разделе Несложный ввод/вывод.

       waddch (win, ch)

       mvwaddch (win, y, x, ch)

       waddstr (win, str)

       mvwaddstr (win, y, x, str)

       wprintw (win, fmt[,arg...])

       mvwprintw (win, y, x, fmt[,arg...])

       wmove (win, y, x)

       wclear (win)
       
       werase (win)

       wclrtoeol (win)
       
       wclrtobot (win)

       wrefresh (win)

Как видно из описаний, эти подпрограммы отличаются от тех, которые работают с stdscr, только своими именами и добавлением аргумента win. Заметьте, что у подпрограмм, чьи имена начинаются на mvw, аргумент win предшествует координатам y, x, хотя, судя по их именам, следовало бы ожидать обратного. См. в curses(3X) дополнительные данные об этих подпрограммах, вариантах подпрограмм getch(), getstr() и другую информацию, касающуюся работы с окнами.

Все подпрограммы с буквой w в имени могут использоваться и со спецокнами, за исключением wrefresh() и wnoutrefresh() (см. ниже). Вместо них со спецокнами нужно использовать соответственно prefresh() и pnoutrefresh().

2.8.2. Подпрограммы wnoutrefresh( ) и doupdate( )

Как было сказано ранее, подпрограмма refresh() пересылает данные с stdscr на экран терминала. refresh() является макросом, который расширяется в wrefresh (stdscr), о чем также упоминалось в разделах Что нужно программе для работы с curses и Еще об окнах и подпрограмме refresh().

Подпрограмма wrefresh() предназначена для пересылки данных из окна (будь то stdscr или созданное пользователем) на экран терминала; она вызывает подпрограммы wnoutrefresh() и doupdate(). Аналогичным образом, prefresh() пересылает данные из спецокна на экран, обращаясь к pnoutrefresh() и doupdate().

Пользуясь wnoutrefresh() или pnoutrefresh() (здесь мы для простоты рассмотрим только первую из них) и doupdate(), Вы можете обновлять экран с большей эффективностью, чем обращаясь к wrefresh(). wrefresh() сначала вызывает wnoutrefresh(), которая копирует указанное окно в структуру данных, называемую виртуальным экраном. Виртуальный экран содержит то, что программа собирается вывести на терминал. Вызвав wnoutrefresh(), wrefresh() затем обращается к doupdate(), которая сравнивает виртуальный экран с физическим и производит обновление последнего. Если Вы хотите отобразить содержимое сразу нескольких окон, вызовы wrefresh() приведут к нескольким обращениям к wnoutrefresh() и doupdate(), то есть к нескольким актам вывода данных на экран. Однако, вызвав wnoutrefresh() для каждого окна, а затем один раз doupdate(), можно уменьшить общее количество передаваемых символов и необходимого для передачи времени процессора. Нижеследующая программа использует doupdate() лишь однажды.

       #include <curses.h>
       
       main ()
       {
         WINDOW *w1, *w2;
         initscr ();
         w1 = newwin (2, 6, 0, 3);
         w2 = newwin (1, 4, 5, 4);
         waddstr (w1, "Bulls");
         wnoutrefresh (w1);
         waddstr (w2, "Eye");
         wnoutrefresh (w2);
         doupdate ();
         endwin ();
       }
В начале этой программы объявляются новые окна. Операторы
         w1 = newwin (2, 6, 0, 3);
         w2 = newwin (1, 4, 5, 4);

объявляют два окна, которые называются w1 и w2, передавая некоторые описания подпрограмме newwin(), которая подробнее описана ниже. Ниже показано действие wnoutrefresh() и doupdate() на эти два окна и на виртуальный и физический экраны.

2.8.3. Новые окна

Далее следуют описания подпрограмм newwin() и subwin(), которые применяются для создания новых окон. В curses(3X) описаны подп- рограммы newpad() и subpad() для создания новых спецокон.

 

newwin( )

СИНТАКСИС
 
#include <curses.h>
       
WINDOW *newwin (nlines, ncols, begin_y, begin_x)
int nlines, ncols, begin_y, begin_x;
ОПИСАНИЕ
  newwin() возвращает указатель на новое окно с новой областью данных.

Аргументы nlines и ncols задают размер нового окна.

Аргументы begin_y и begin_x задают координаты точки на экране [относительно (0, 0)], на которую отображается левый верхний угол окна.

ПРИМЕР
  См. пример программы, работающей с двумя окнами и рисунок выше. См. также программу window в разделе Примеры программ, работающих с curses.

 

subwin( )

СИНТАКСИС
 
#include <curses.h>
       
WINDOW *subwin (orig, nlines, ncols, begin_y, begin_x)
WINDOW *orig;
int nlines, ncols, begin_y, begin_x;
ОПИСАНИЕ
  subwin() возвращает указатель на новое окно, которое является частью ранее определенного окна, orig.

Аргументы nlines и ncols задают размер нового окна.

Аргументы begin_y и begin_x задают координаты левого верхнего угла окна на экране терминала.

Как подокна, так и сами окна могут перекрываться.

Предостережение

На момент выпуска данного руководства не работало определение подокон у подокон.
ПРИМЕР
 
       #include <curses.h>
       
       main()
       {
         WINDOW *sub;
       
         initscr ();
         box (stdscr, 'w', 'w');  /* О box() см. curses(3X) */
         mvaddstr (stdscr, 7, 10, "------- это 10,10");
         mvaddch (stdscr, 8, 10, '');
         mvaddch (stdscr, 9, 10, 'v');
         sub = subwin (stdscr, 10, 20, 10, 10);
         box (sub, 's', 's');
         wnoutrefresh (stdscr);
         wrefresh (sub);
         endwin ();
       }

Эта программа заполняет границу stdscr (то есть края экрана терминала) символами w, а границу подокна sub - символами s. Другой пример см. в программе window раздела Примеры программ, работающих с curses.

2.9. Прочие возможности пакета curses

Умея пользоваться основными подпрограммами curses для ввода/вывода на экран и управления окнами, можно создавать терминальные программы, удовлетворяющие потребности большинства пользователей. Вместе с тем, в curses имеются подпрограммы, которые позволяют делать больше, чем просто вводить и выводить символы и работать с окнами. На следующих нескольких страницах кратко описываются некоторые из этих подпрограмм, которые помогут Вам выводить простые графические изображения, использовать программируемые метки терминала и обращаться из одной программы к нескольким терминалам.

Прежде, чем использовать эти возможности, следует освоить подпрограммы ввода/вывода и управления окнами, которые были рассмотрены выше в этой главе, а также в руководстве curses(3X).

Предостережение

Подпрограммы, описываемые далее в разделах Линии на экране и прочая графика и Использование программируемых меток, имеются только в системе UNIX V версия 3.1. Программа, работающая с ними, возможно, не сможет выполняться на более ранних версиях системы UNIX. Для работы с этими подпрограммами необходимо иметь систему UNIX V версия 3.1 и соответствующую версию библиотеки curses.

2.9.1. Линии на экране и прочая графика

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

Для работы с альтернативной кодировкой в curses-программе, необходимо передать подпрограмме waddch() (или подобной) последовательность символов, чьи имена начинаются с ACS_. Например, ACS_ULCORNER - это переменная для символа, изображающего левый верхний угол. Если терминал поддерживает этот символ, то в ACS_ULCORNER будет результат операции ИЛИ (|) соответствующего значения и битной маски A_ALTCHARSET. Если такого графического символа нет, используется символ из стандартного набора ASCII, аппроксимирующий требуемое изображение. Например, для аппроксимации ACS_HLINE, горизонтальной черты, используется - (знак минус). Если близкая аппроксимация невозможна, используется +, знак плюс. Все стандартные имена ACS_ и аппроксимирующие их символы перечислены в curses(3X).

Мы приведем часть текста программы, которая использует графические символы. В примере вызывается подпрограмма box(), входящая в пакет curses, для того, чтобы окружить меню на экране рамкой. По умолчанию box() использует графические символы, но по выбору пользователя может рисовать знаками и - [см. curses(3X)]. Если меню, изображенное в рамке, имеет продолжение вверх или вниз за пределы рамки, то для указания этого выводятся стрелки соответственно вверх или вниз (ACS_UARROW и ACS_DARROW).

         box (menuwin, ACS_VLINE, ACS_HLINE);
                    ...
         /* Вывод стрелок вверх/вниз */
         wmove (menuwin, maxy, maxx - 5);
       
         /* вывод стрелки вверх или горизонтальной черты */
         if (moreabove)
           waddch(menuwin, ASC_UARROW);
         else
           waddch (menuwin, ACS_HLINE);
       
         /* вывод стрелки вниз или горизонтальной черты  */
         if (morebelow)
           waddch (menuwin, ASC_DARROW);
         else
           waddch (menuwin, ACS_HLINE);

Приведем еще один пример. Поскольку символ для стрелки вниз (например, строчная v) не очень выделяется на экране, если на нем много строчных букв, этот символ можно заменить на прописную V.

         if ( ! (ACS_DARROW & A_ALTCHARSET))
           ACS_DARROW = 'V';

Более подробную информацию см. в Справочнике программиста, curses(3X).

2.9.2. Использование программируемых меток

Большинство терминалов поддерживает программируемые метки в нижней части своего экрана. На клавиатуре этим меткам обычно соответствует функциональные клавиши. Меток обычно восемь, каждая длиной в восемь знаков и высотой в одну-две строки.

Библиотека curses содержит подпрограммы, поддерживающие единую модель из восьми программируемых меток. Если терминал их не имеет, нижняя строка его экрана превращается в область программируемых меток. Для использования в curses-программах таких меток, нет необходимости иметь на клавиатуре соответствующие им функциональные клавиши.

Далее кратко описываются большинство подпрограмм curses, применяющихся при работе с программируемыми метками: slk_init(), slk_set(), slk_refresh(), slk_noutrefresh(), slk_clear() и slk_restore().

Если Вы пользуетесь программируемыми метками в curses-программе, Вам необходимо перед initscr() вызвать подпрограмму slk_init(). При этом устанавливается внутренний флаг, указывающий initscr(), что программируемые метки используются. Если initscr() обнаружит, что программируемые метки отсутствуют на терминале, либо их меньше восьми, либо их длина менее восьми символов, она удаляет нижнюю строку stdscr, чтобы использовать ее для размещения программируемых меток. При этом размер stdscr и значение переменной LINES уменьшается на единицу. Разумно написанная программа, использующая переменные LINES и COLS, будет выполняться правильно, как если бы этой строки вообще не было бы на экране.

slk_init() имеет один аргумент, определяющий способ размещения меток на экране в случае использования для этого строки из stdscr. Выбор возможен между размещением группами 3-2-3, как это принято на терминалах AT&T, либо 4-4, как на терминалах Хьюлетт-Паккард. Подпрограммы curses определяют положение и размер меток в соответствии с указанным способом, причем максимально возможная длина метки - восемь символов.

Подпрограмма slk_set() имеет три аргумента: номер метки (1-8), текст, помещаемый в метку (не более восьми символов), выравнивание этого текста в поле метки (0 - выравнивание влево, 1 - центрирование, 2 - выравнивание вправо).

Подпрограмма slk_noutrefresh() подобна wnoutrefresh() тем, что она обновляет содержимое внутреннего образа экрана, но не выводит данные на реальный экран. Поскольку после ее вызова обычно вызывают wrefresh(), именно slk_noutrefresh(), как правило, используется для вывода меток на экран терминала.

Подобно тому, как wrefresh() эквивалентна последовательным вызовам wnoutrefresh() и doupdate(), подпрограмма slk_refresh() эквивалентна последовательным вызовам slk_noutrefresh() и doupdate().

Чтобы программируемые метки не мешали работе в среде shell, перед обращением к endwin() можно вызвать slk_clear(), которая удаляет программируемые метки с экрана и вызывает doupdate(). Программируемые метки можно восстановить на экране функцией slk_restore(). Подробнее о подпрограммах для работы с программируемыми метками см. curses(3X).

2.9.3. Работа с несколькими терминалами сразу

Curses-программа может выводить данные сразу на несколько терминалов. Это может оказаться полезным при разработке программ, которые представляют собой один процесс и обращаются к одной базе данных, например, игр для нескольких участников.

Писать программы, работающие с несколькими терминалами, - дело нелегкое, и подпрограммы curses не решают всех возникающих при этом проблем. Например, сами программы, а не подпрограммы библиотеки, должны определять имена файлов для терминалов и типы этих терминалов. Стандартный способ с использованием переменной окружения $TERM не срабатывает, поскольку каждый процесс может знать только о своем собственном окружении.

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

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

Ссылки на терминал в curses-программе имеют тип *SCREEN. Новый терминал инициализируется путем обращения к newterm (type, outfd, infd). newterm возвращает указатель на новый терминал, type - это цепочка символов, содержащая тип используемого терминала. outfd является указателем на файл [*FILE, stdio(3S)], который используется для вывода на терминал, а infd указывает на файл для ввода с терминала. Вызов newterm() заменяет обычное обращение к initscr(), которое расширяется в newterm (getenv ("TERM"), stdout, stdin).

Для изменения текущего терминала необходимо вызвать set_term (sp), где sp указывает на терминал, который должен стать текущим. set_term() возвращает указатель на терминал, который был текущим на момент ее вызова.

Важно понять, что каждый терминал будет иметь свои собственные режимы работы и окна. Каждый терминал должен быть проинициализирован соответствующим вызовом newterm(). Для каждого терминала отдельно должны быть установлены режимы работы, например, cbreak() или noecho(). Равным образом для каждого терминала отдельно должны вызываться endwin() и refresh(). Ниже изображен типичный сценарий рассылки сообщения на несколько терминалов.

         for (i=0; i<nterm; i++) {
           set_term (terms[i]);
           mvaddstr (0, 0, "Важное сообщение");
           refresh ();
         }

Более подробный пример см. в программе two раздела Примеры программ, работающих с curses.

 

3. ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММ ПАКЕТА TERMINFO

Иногда могут понадобиться подпрограммы более низкого уровня, чем те, которые предлагает пакет curses. Такие подпрограммы содержатся в пакете terminfo. Они не работают непосредственно с экраном терминала, а дают пользователю возможность узнать значения характеристик терминала и последовательности символов, которые терминалом управляют.

Подпрограммы terminfo полезно использовать в трех случаях. Вопервых, если Вам нужны только некоторые возможности терминала, например, выделение текста на экране. Во-вторых, если нужно написать фильтр. Типичный фильтр только преобразует входной поток, без очистки экрана или перемещения курсора. Если вид преобразования зависит от терминала, и экран не должен очищаться, то стоит воспользоваться подпрограммами terminfo. В-третьих, если Вы пишете специальную программу для посылки специальной строки на терминал, например, для программирования функциональной клавиатуры, установки позиций табуляции, передачи данных на принтер, или для работы со строкой состояния. За исключением этих трех случаев, Вам лучше не пользоваться подпрограммами terminfo: curses содержит подпрограммы более высокого уровня, использование которых повысит степень переносимости Ваших программ на другие системы UNIX и другие терминалы.

Примечание

Вам не следует использовать подпрограммы terminfo, кроме трех перечисленных случаев, поскольку подпрограммы curses обеспечивают реакцию на изъяны и сбои физических терминалов. При работе с подпрограммами terminfo Вам придется самостоятельно реагировать на возникающие ситуации. Кроме того, эти подпрограммы меняются и могут стать несовместимыми с прежними.

3.1. Что нужно программе для работы с terminfo

Как правило, программа, работающая с terminfo, включает файлы и подпрограммы, которые перечислены ниже:

       #include <curses.h>
       #include <term.h>
        . . .
         setupterm ((char*) 0, 1, (int*) 0);
          . . .
         putp (clear_screen);
          . . .
         reset_shell_mode ();
         exit (0);

Файлы <curses.h> и <term.h> нужны, поскольку они содержат определения текстовых объектов, чисел и флагов, которые используются подпрограммами terminfo. setupterm() занимается инициализацией. Передача ей значений (char*) 0, 1 и (int*) 0 обеспечивает установку разумных режимов. Если setupterm() не может распознать тип используемого терминала, она выводит сообщение об ошибке и завершается. reset_shell_mode() делает примерно то же, что и endwin(), и должна вызываться перед завершением terminfo-программы. При вызове setupterm() определяются значения глобальных переменных, например clear_screen. Их значения могут быть выведены на экран входящими в terminfo подпрограммами putp() или tputs(), что обеспечивает пользователю дополнительные возможности по контролю терминала. Такую строку не следует выводить подпрограммой printf(3S) из библиотеки языка C, поскольку в ней содержится информация об использовании символов-заполнителей. Программа, пытающаяся вывести ее таким образом, может завершиться аварийно, если терминал требует использования заполнителей, или если он использует протокол xon/xoff.

На уровне terminfo подпрограммы более высокого уровня, например, addch() и getch(), недоступны. Вам придется самостоятельно решать проблему вывода на экран. Список характеристик и их описаний см. в terminfo(4), список подпрограмм terminfo см. в curses(3X).

3.2. Компиляция и выполнение программ, которые используют terminfo

Общий формат командной строки для компиляции, как и особенности запуска программ, использующих terminfo, те же, что и для любой другой curses-программы. Подробности см. в разделах Компиляция программы, которая использует curses и Выполнение программы, которая использует curses.

3.3. Пример программы, работающей с terminfo

Программа termhl - это простой пример использования подпрограмм terminfo. Она является вариантом программы highlight (см. раздел Примеры программ, работающих с curses), в котором не используются возможности curses более высокого уровня. Программу termhl можно использовать в качестве фильтра, вставляющего последовательности символов, необходимые для включения режимов подсветки и подчеркивания и для отключения всех атрибутов.
       /* Версия программы highlight уровня terminfo */
       
       #include <curses.h>
       #include <term.h>
       
       int ulmode = 0;        /* Режим подчеркивания */
       
       main (argc, argv)
       int argc;
       char **argv;
       {
         FILE *fd;
         int c, c2;
         int outch();
       
         if (argc > 2) {
           fprintf (stderr, "Откуда: termhl [file]\n");
           exit(1);
         }

         if (argc == 2) {
           fd = fopen (argv [1], "r");
           if (fd == NULL) {
             perror (argv [1]);
             exit(2);
           }
         }
         else {
           fd = stdin;
         }
         setupterm ((char*) 0, 1, (int*) 0);

         for (;;) {
           c = getc (fd);
           if (c == EOF) break ();
           if (c == '\') {
             c2 = getc (fd);
             switch (c2) {
               case 'B':
                 tputs (enter_bold_mode, 1, outch);
                 continue;
               case 'U':
                 tputs (enter_underline_mode, 1, outch);
                 ulmode = 1;
                 continue;
               case 'N':
                 tputs (exit_attribute_mode, 1, outch);
                 ulmode = 0;
                 continue;
             }
             putch (c);
             putch (c2);
           }
           else putch (c);
         }

         fclose (fd);
         fflush (stdout);
         resetterm ();
         exit (0);
       }

       /* Функция putch подобна putchar,           */
       /* но проверяет необходимость подчеркивания */
       
       putch (c)
       int c;
       {
         outch (c);
         if (ulmode && underline_char) {
           outch ('\b');
           tputs (underlihe_char, 1, outch);
         }
       }

       /* outchar передается tputs в качестве параметра      */
       /* как функция, которую нужно вызывать вместо putchar */
       
       outch (c)
       int c;
       {
         putchar (c);
       }

Познакомимся поближе с подпрограммами terminfo на примере использования функции tputs (cap, affcnt, outc) в этой программе. tputs() добавляет символы-заполнители. Некоторые терминалы имеют возможность задерживать вывод. Их описания в базе данных terminfo, быть может, содержат цепочки, подобные $<20>, что означает задержку на 20 миллисекунд (см. ниже раздел Указание характеристик терминала). tputs генерирует достаточное для задержки на это время количество символов-заполнителей.

tputs() имеет три параметра. Первый является цепочкой символов, определяющей характеристику (атрибут вывода), которую необходимо реализовать. Второй - количество затрагиваемых этим строк. (Некоторые запросы требуют добавления символов-заполнителей в количестве, зависящем от этого параметра. Например, запрос на вставку строки, insert_line, может привести к копированию всех строк ниже текущей, для чего нужно время, пропорциональное количеству копируемых строк. Если ни одна строка не затрагивается, в качестве affcnt используется 1. Единица вместо нуля берется для надежности, так как affcnt умножается на некоторый интервал времени, а умножение чего-либо на 0 дает 0). Третий параметр - это подпрограмма, вызываемая для вывода каждого символа.

Для понимания желательности использования подпрограмм уровня curses вместо подпрограмм уровня terminfo (там, где это возможно), полезно обратить внимание на проверку значения underline_char в примере. Некоторые терминалы имеют управляющий символ, указывающий начало подчеркивания, и еще один символ, указывающий конец подчеркивания. Другим необходимо указание кода подчеркивания при выводе каждого символа. Программа termhl хранит значение этого режима, и, если необходимо, выводит underline_char для подчеркивания очередного символа. Из-за наличия такого рода мелких деталей, с которыми программы уровня terminfo должны разбираться самостоятельно, и предпочтительнее работать на уровне curses.

Программа termhl была написана для иллюстрации применения подпрограмм terminfo, поэтому она получилась сложнее, чем могла бы быть. Вместо непосредственного вывода enter_bold_mode, enter_underline_mode и exit_attribute_mode можно было бы использовать подпрограмму vidattr [см. curses(3X)]. Фактически, в последнем случае программа была бы более надежна, поскольку существует несколько способов изменения атрибутов вывода.

 

4. ИСПОЛЬЗОВАНИЕ БАЗЫ ДАННЫХ TERMINFO

База данных terminfo содержит описания многих терминалов, с которыми могут работать как подпрограммы curses, так и некоторые средства системы UNIX, например vi(1). Каждое описание терминала представляет собой скомпилированный файл, содержащий имена, под которыми этот терминал известен, и группу разделенных запятыми полей, описывающих возможные действия и характеристики терминала. В этом разделе описываются база данных terminfo, средства для работы с ней и ее связь с библиотекой curses.

4.1. Создание описания терминала

База данных terminfo содержит описания многих наиболее часто применяющихся терминалов. Может, однако, оказаться, что Вы пожелаете написать curses-программу, предназначенную для выполнения на терминале, который еще не описан. В этом случае Вам придется создать его описание.

В общем случае для генерации описания терминала необходимо:

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

    Иногда генерировать и тестировать описание терминала легче по частям. Такие последовательные тесты помогут выявить Ваши слабые стороны в работе по описанию терминалов. Кроме того, можно облегчить генерацию, корректируя уже существующее описание похожего терминала (не следует забывать девиз системы UNIX: опираться на работу других).

    Далее мы опишем каждый шаг построения описания некоторого условного терминала myterm.

    4.1.1. Название терминала

    Название терминала - это информация, с которой начинается его описание в terminfo. В действительности, название может включать несколько имен, разделенных вертикальной чертой (|). Первое имя должно представлять собой наиболее часто используемое сокращение. Последним должно указываться имя, полностью идентифицирующее терминал. Обычно, это название, которым пользуется фирма-производитель. Все имена между первым и последним должны быть известными синонимами имени терминала. Все имена, кроме последнего, записываются строчными буквами и не должны содержать пробелов. Естественно, последнее имя как можно более точно воспроизводит название фирмы-производителя.

    Приведем название из описания терминала AT&T Teletype 5420:

           5420|att5420|AT&T Teletype 5420,
    

    Обратите внимание, что в начале идет наиболее часто используемое сокращение, а в конце - официальное название терминала. Заметьте также, что название оканчивается запятой.

    Приведем название для нашего условного терминала myterm:

           myterm|mytm|mine|fancy|terminal|My FANCY Terminal,
    

    Имена терминалов должны удовлетворять обычным соглашениям об именах. Эти соглашения касаются, в частности, начального имени, например, 5425 или myterm. Это имя не должно содержать странных символов (например, минусов), которые могут не быть распознаны как часть синонима имени терминала. Возможные режимы оборудования и предпочтения пользователя указываются путем добавления имени и "индикатора режима" в конце имени. Например, для нашего фиктивного терминала режим с большим числом символов в строке (что обозначается через -w) будет записан в виде myterm-w. Другие индикаторы подробно обсуждаются в term(5).

    4.1.2. Выяснение характеристик терминала

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

    4.1.3. Указание характеристик терминала

    После того, как Вы уяснили характеристики терминала, их необходимо указать в его описании. Описание имеет вид последовательности разделенных запятыми полей, каждое из которых содержит принятое в terminfo сокращение названия характеристики и, в некоторых случаях, ее значение для данного терминала. Например, для указания способности терминала подавать звуковой сигнал используется сокращение bel. На большинстве терминалов инструкцией, передаваемой для подачи звукового сигнала, является CTRL+G. Таким образом, характеристика возможности подачи звукового сигнала изображается в описании терминала в виде bel=^G,.

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

    Полный список характеристик, которые можно указывать в описании терминала см. в terminfo(4). В этом списке приводятся название характеристики, сокращенное название, которое используется в базе данных, двухбуквенный код, соответствующий этой характеристике в устаревшей базе данных termcap, а также краткое описание характеристики. Сокращение, которое необходимо указать в описании терминала см. в колонке "Сокращение".

    Примечание

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

    Значение характеристики может задаваться клавишей (например, CTRL+G), числом, или цепочкой символов, определяющей последовательность операций, которые нужно выполнить, чтобы добиться необходимого эффекта. Для указания типа значения после названия характеристики в описании терминала могут использоваться некоторые специальные символы, а именно:

     #  Указывает, что далее следует числовое значение характеристики. Например, количество столбцов может определяться в виде cols#80.
     =  Указывает, что значение характеристики является цепочкой символов. Эта цепочка в действительности может быть последовательностью команд, требующих от терминала выполнения определенных действий. В таких цепочках могут использоваться некоторые специальные символы, а именно:
     ^  Обозначает управляющий символ. Например, звуковой сигнал подается передачей CTRL+G, что записывается в виде ^G.
     \e или \E  В сопровождении еще каких-либо символов обозначают escape-последовательности. Например, \EC передается на терминал в виде ESC C.
     \n  Обозначает перевод строки.
     \l  Обозначает переход к новой строке.
     \r  Обозначает возврат каретки.
     \t  Обозначает горизонтальную табуляцию.
     \b  Обозначает возврат на символ назад.
     \f  Обозначает переход к новой странице.
     \s  Обозначает пробел.
     \nnn  Обозначает символ с восьмеричным кодом nnn, где nnn может иметь от одной до трех цифр.
     $< >  Эти символы используются для обозначения задержки в миллисекундах. Длительность задержки заключается в угловые скобки (<>). Она может быть целым или десятичным числом с одной цифрой после точки. После числа может следовать звездочка (*). Звездочка указывает, что длительность задержки пропорциональна количеству вовлеченных в операцию строк. Например, задержка в 20 миллисекунд на строку указывается в виде $<20*>. Более подробную информацию о задержке и символах-заполнителях см. в terminfo(4).

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

           .bel=^G,
    
    Теперь, когда мы узнали, как описываются характеристики терминала, опишем их для myterm. Мы рассмотрим характеристики экрана, клавиатуры, основные и параметрические характеристики.

    4.1.3.1. Основные характеристики

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

    Объединив последовательность имен (см. раздел Название терминала) и описание упомянутых характеристик, мы получим следующее описание для базы данных terminfo:

           mytermmytmminefancyterminalMy FANCY Terminal,
                   am, bel=^G, cols#80, lines#30, xon,
    

    4.1.3.2. Характеристики экрана

    Эти характеристики управляют содержимым экрана. Пусть терминал myterm имеет перечисленные ниже характеристики. Опять-таки, в скобках после описания характеристики дается ее сокращение.

    Описание терминала myterm, включающее эти характеристики, будет выглядеть так:

           mytermmytmminefancyterminalMy FANCY Terminal,
                   am, bel=^G, cols#80, lines#30, xon,
                   cr=^M, cuu1=^K, cud1=^G, cub1=^H, cuf1=^L,
                   smso=\ED, rmso=\EZ, el=\EK$<3>, ind=\n,
    

    4.1.3.3. Характеристики клавиатуры

    Эти характеристики представляют собой последовательности симво- лов, генерируемые при нажатии клавиш на клавиатуре терминала. Большинство терминалов имеет ряд специальных клавиш, по меньшей мере, стрелки и клавиши забоя. Пусть наш гипотетический терми- нал имеет несколько таких клавиш, которые генерируют следующие последовательности:

    Добавим эту информацию к нашему описанию:

           mytermmytmminefancyterminalMy FANCY Terminal,
                   am, bel=^G, cols#80, lines#30, xon,
                   cr=^M, cuu1=^K, cud1=^G, cub1=^H, cuf1=^L,
                   smso=\ED, rmso=\EZ, el=\EK$<3>, ind=\n,
                   kbs=^H, kcuu1=\E[A, kcud1=\E[B, kcuf1=\E[C,
                   kcub1=\E[D, khome=\E[H,
    

    4.1.3.4. Параметризованные цепочки

    Характеристики, значения которых задаются параметризованными цепочками, могут иметь параметры. Например, таковы характеристики, описывающие позиционирование курсора на экране, или включение комбинации режимов отображения. Для позиционирования курсора используется характеристика cup, при этом передаются два параметра: номер строки и номер столбца. В случае таких характеристик, как cup или sgr (установка атрибутов), параметры передаются через входящую в terminfo подпрограмму tparm().

    Аргументы параметрических характеристик обрабатываются согласно включенным в описания этих характеристик специальным последова- тельностям символов, которые начинаются с %. Эти последовательности похожи на те, которые используются в printf(3S). Кроме того, данные можно записать в стек и производить над ними операции, которые записываются в виде, близком к обратной польской записи. Как мы упомянули, cup имеет два аргумента: номер строки и номер столбца. sgr имеет девять аргументов, по одному на каждый из атрибутов отображения. Список аргументов sgr и порядок их следования, а также примеры см. в terminfo(4).

    Пусть на нашем терминале курсор позиционируется последовательностью, состоящей из символов ESC [, за которыми следуют номера строки и столбца, разделенные точкой с запятой, а в конце идет H. Координаты задаются относительно 1, а не нуля. Так, чтобы передвинуть курсор в позицию (5, 18) относительно (0, 0), необ- ходимо передать последовательность \E[6;19H.

    Целочисленные аргументы записываются в стек последовательностью %p, за которой следует номер аргумента, например, %p2 для записи в стек второго аргумента. %i - это краткая запись для увеличения первых двух аргументов. Для вывода числа из вершины стека в десятичном виде используется %d, как и в printf. Последовательность cup нашего терминала строится следующим образом:

     cup=   Пояснения 
     \E[   вывести ESC [ 
     %i   увеличить оба аргумента 
     %p1   записать первый аргумент (номер строки) в стек 
     %d   вывести номер строки в десятичном виде 
     ;   вывести точку с запятой 
     %p2   записать второй аргумент (номер столбца в стек) 
     %d   вывести номер столбца в десятичном виде 
     H   вывести завершающий цепочку символ H 

    или

                   cup=\E[%i%p1%d;%p2%dH,
    

    Добавив это к нашему описанию, получим:

           mytermmytmminefancyterminalMy FANCY Terminal,
                   am, bel=^G, cols#80, lines#30, xon,
                   cr=^M, cuu1=^K, cud1=^G, cub1=^H, cuf1=^L,
                   smso=\ED, rmso=\EZ, el=\EK$<3>, ind=\n,
                   kbs=^H, kcuu1=\E[A, kcud1=\E[B, kcuf1=\E[C,
                   kcub1=\E[D, khome=\E[H,
                   cup=\E[%i%p1%d;%p2%dH,
    

    Подробнее о параметризованных цепочках см. terminfo(4).

    4.1.4. Компиляция описания

    Описания терминалов в базе данных terminfo переводятся из исходного в компилированный формат компилятором tic(1M).

    Исходный файл с описанием обычно имеет расширение .ti. Например, описание myterm будет называться myterm.ti. Скомпилированное описание myterm обычно помещается в /usr/lib/terminfo/m/ myterm, так как название терминала начинается на m. myterm будет также иметь синонимы, например, /f/fancy. Если перед компиляцией установлено и помещено в окружение значение переменной $TERMINFO, скомпилированное описание будет помещено в каталог $TERMINFO. В этом случае все программы будут искать описание терминала сначала в каталоге $TERMINFO, а затем уже в подразумеваемом каталоге /usr/lib/terminfo. Общий формат командной строки для запуска компилятора tic таков:

           tic  [-v[число]] [-c] файл
    

    Опция -v требует выводи трассировочной информации о ходе компиляции файла. Опция -c задает поиск ошибок в описании, она может использоваться одновременно с -v. Если нужно скомпилировать сразу несколько файлов, следует прежде воспользоваться командой cat(1) для их объединения. Приведенная ниже командная строка задает компиляцию описания terminfo для нашего гипотетического терминала:

           tic -v myterm.ti
           # в ходе компиляции выдается трассировочная информация
    

    Подробную информацию о компиляторе см. в Справочнике администратора, tic(1M).

    4.1.5. Тестирование описания

    Рассмотрим три способа тестирования описания терминала. Во-первых, можно установить значение переменной окружения $TERMINFO равным имени каталога, содержащего это описание. Если программы выполняются на новом терминале так же, как они делали это на других терминалах, новое описание пригодно для использования.

    Во-вторых, можно проверить правильность заполнения экрана при вставке строк, для чего закомментировать характеристику xon в описании терминала и редактировать при помощи vi(1) большой файл (не менее ста строк) при скорости передачи 9600 бод, если это возможно. Удалите около 15 строк в середине экрана и быстро нажмите несколько раз u (отменить операцию). Если содержимое экрана портится, то, вероятно, нужно увеличить время задержки. Примерно такой же тест можно использовать для проверки вставки символа.

    В-третьих, можно воспользоваться командой tput(1). Эта команда выводит значение характеристики - числовое или текстовое, в зависимости от ее типа. Если характеристика булева, tput ничего не выводит, но возвращает код завершения (0, если истина, 1, если ложь). Общий формат команды tput таков:

           tput  [-T тип] характеристика
    
    Опция -Tтип задает тип терминала, о котором вы запрашиваете информацию. Обычно эту опцию указывать не требуется, так как по умолчанию в качестве имени терминала берется значение переменной окружения $TERM. Поле характеристика указывает, значение какой характеристики надо извлечь из базы данных terminfo.

    Следующая командная строка показывает, как выводится на экран последовательность для очистки экрана:

           tput clear
           # экран терминала очищается
    

    Следующая командная строка показывает, как отобразить на терминале количество колонок, имеющихся на его экране:

           tput cols
           # здесь появится количество колонок
    

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

    4.2. Печать и сравнение описаний в базе данных terminfo

    Иногда бывает необходимо сравнить описания двух терминалов или просмотреть описание, не обращаясь к каталогу, содержащему исходные тексты описаний terminfo. В обоих случаях можно воспользоваться командой infocmp(1M). Сравнить два описания одного и того же терминала можно, например, так:

           mkdir /tmp/old /tmp/new
           TERMINFO=/tmp/old tic old5420.ti
           TERMINFO=/tmp/new tic new5420.ti
           infocmp -A /tmp/old -B /tmp/new -d 5420 5420
    

    Эта последовательность команд сравнивает старое и новое описания терминала 5420.

    Чтобы получить исходный текст описания terminfo для 5420, введите

           infocmp -I 5420
    

    4.3. Преобразование termcap-описания в terminfo-описание

    Предостережение

    База данных terminfo разработана в качестве замены базы данных termcap. Такой переход нельзя выполнить мгновенно, поскольку с использованием termcap (и для работы с ней) написано множество программ и процессов. Любой переход от termcap к terminfo требует некоторого опыта работы с обеими базами данных. С описаниями в этих базах данных необходимо обращаться весьма осторожно, поскольку от них зависит правильное функционирование Вашего терминала. Команда captoinfo(1M) преобразует описание в формате termcap(4) в описание в формате terminfo(4). captoinfo получает файл в формате termcap и выводит эквивалентное описание в формате terminfo на стандартный вывод. Например, командная строка
           captoinfo /etc/termcap
    

    преобразует файл /etc/termcap в исходный текст в формате terminfo, с сохранением комментариев и другой информации, не относящейся собственно к описанию, если она присутствует. Командная строка

           captoinfo
    

    ищет описание текущего терминала в базе данных termcap (они задаются значениями переменных окружения $TERM и $TERMCAP соответственно) и преобразует его в формат terminfo.

    Если нужны описания терминала как в формате termcap, так и в формате terminfo, можно хранить только описание terminfo и использовать infocmp -C для получения описания в формате termcap.

    Если Вы компилировали программы командой cc(1) с опциями -ltermcap или -ltermlib, они будут работоспособны и в дальнейшем. Однако, вместо этих опций следует использовать -lcurses.

     

    5. ПРИМЕРЫ ПРОГРАММ, РАБОТАЮЩИХ С CURSES

    Далее приводятся примеры, демонстрирующие использование подпрограмм curses.

    5.1. Программа editor

    Здесь подпрограммы curses используются для создания редактора текстов. Для простоты программа editor хранит буфер в stdscr; ясно, что в настоящем редакторе следовало бы иметь отдельную структуру для буфера. В этой программе есть и другие упрощения: не предусматривается редактирование файлов, содержащих больше строк, чем помещается на экране, либо со строками, длина которых больше длины строки на экране; считается, что файл не содержит управляющих символов.

    Об этой программе стоит сказать следующее. Во-первых, она использует подпрограммы move(), mvaddstr(), flash(), wnoutrefresh(), clrtoeol(). Эти подпрограммы рассматривались в разделе Использование подпрограмм пакета curses.

    Во-вторых, в ней применяются некоторые подпрограммы curses, о которых мы ранее не упоминали. Например, функция, осуществляющая запись файла, обращается к подпрограмме mvinch(), которая возвращает символ, находящийся в указанной позиции окна. Структура данных, используемая для записи файла, не содержит информации о длине строк и их количестве в файле, поэтому при записи уничтожаются хвостовые пробелы. Программа использует также подпрограммы insch(), delch(), insertln(), deleteln(), которые вставляют или удаляют символ или строку. Информацию об этих подпрограммах см. в curses(3X).

    В-третьих, этот редактор воспринимает в качестве команд как специальные клавиши, так и символы ASCII. С одной стороны, для новичков более удобен в освоении редактор, использующий специальные клавиши. Им легче использовать стрелки для перемещения курсора, чем запомнить, что букве h соответствует движение влево, j - вниз, k - вверх, а l - вправо. С другой стороны, опытные пользователи обычно предпочитают использовать символы ASCII, так как в этом случае не нужно переносить пальцы в ту часть клавиатуры, где находятся специальные клавиши.

    Примечание

    Ваша curses-программа сможет работать с более широким набором терминалов, если в ней с каждой специальной клавишей будет связан символ ASCII, поскольку не на каждом терминале клавиатура включает, например, стрелки.

    В-четвертых, команда CTRL+L выполняет функцию, которую должны иметь большинство curses-программ. Случается так, что другие программы выводят что-либо на экран (например, при передаче сообщений), либо экран портится в результате сбоев оборудования. В этом случае пользователь может ввести CTRL+L, в результате чего экран очищается и заполняется вновь посредством

           wrefresh (curscr)
    

    Наконец, еще одной важной особенностью является то, что вводимые команды заканчиваются нажатием CTRL+D, а не ESC. Было бы соблазнительно использовать ESC, так как эта клавиша является одной из немногих, присутствующих на любой клавиатуре (наряду с RETURN и BREAK). Однако, это может привести к возникновению двусмысленности. Большинство терминалов использует последовательности символов, начинающиеся с ESC для управления терминалом и имеют клавиши, при нажатии на которые передаются такие последовательности. Если программа получает с клавиатуры ESC, она не может различить, нажал ли пользователь ESC или другую функциональную клавишу.

    Editor и другие использующие curses программы решают эту проблему, устанавливая таймер. Если за некоторый интервал времени приходит еще символ и он может быть началом последовательности, которую генерирует функциональная клавиша, программа пытается читать последующие символы до тех пор, пока не будет считана вся последовательность для функциональной клавиши, либо символ, который не может быть частью такой последовательности, либо пока не истечет определенное время. Эта стратегия хороша, но не абсолютно надежна. Пользователь может нажать ESC и сразу за ней еще клавишу, и тогда curses-программа решит, что была нажата функциональная клавиша. Кроме того, ESC не сразу передается обрабатывающей программе, в результате чего последняя медленнее реагирует на нажатие этой клавиши.

    Многие существующие ныне программы используют ESC в качестве одной из основных своих команд, и это положение нельзя изменить, не вызвав недовольства большого количества пользователей. Чтобы использовать функциональную клавиатуру, эти программы должны решать описанную выше проблему двусмысленности - в лучшем случае путем использования таймера. Мораль всего этого ясна: избегайте использования клавиши ESC в Ваших программах.

            /* Программа editor - экранный редактор.
               Пользовательский интерфейс подобен подмножеству vi.
               Для простоты буфер хранится в stdscr */
    
           #include <curses.h>
    
           #define CTRL(c) ((c) & 037)
    
           main (argc, argv)
           int argc;
           char **argv;
           {
             extern void perror(), exit();
             int i, n, l;
             int c;
             int line = 0;
             FILE *fd;
    
             if (argc != 2) {
               fprintf (stderr, "usage: %s file\n", argv [0]);
               exit (1);
             }
    
             fd = fopen (argv [1], "r");
             if (fd == NULL) {
               perror (argv [1]);
               exit (2);
             }
    
             initscr ();
             cbreak ();
             nonl ();
             noecho ();
             idlok (stdscr, TRUE);
             keypad (stdscr, TRUE);
    
             /* Читаем файл */
             while ((c = getc(fd)) != EOF) {
               if (c == '\n') line++;
               if (line > LINES - 2) break;
               addch(c);
             }
             fclose (fd);
    
             move (0, 0);
             refresh ();
             edit ();
    
             /* Записываем файл */
             fd = fopen (argv [1], "w");
             for (l = 0; l < LINES - 1; l++) {
               n = len(l);
               for (i = 0; i < n; i++)
                 putc (mvinch (l, i) & A_CHARTEXT, fd);
               putc('\n', fd);
             }
             fclose(fd);
    
             endwin ();
             exit (0);
           }
    
           len (lineno)
           int lineno;
           {
             int linelen = COLS - 1;
    
             while (linelen >= 0 && mvinch (lineno, linelen) == ' ')
               linelen--;
             return linelen + 1;
           }
    
           /* Глобальное значение текущего положения курсора */
           int row, col;
    
           edit ()
           {
             int c;
    
             for (;;) {
               move (row, col);
               refresh ();
               c = getch ();
    
               /* Команды редактора */
               switch (c) {
    
                 /* hjkl и стрелки: перемещают курсор
                    в указанном направлении */
                 case 'h':
                 case KEY_LEFT:
                   if (col > 0) col--;
                   else flash ();
                   break;
    
                 case 'j':
                 case KEY_DOWN:
                   if (row < LINES - 1) row++;
                   else flash ();
                   break;
    
                 case 'k':
                 case KEY_UP:
                   if (row > 0) row--;
                   else flash ();
                   break;
    
                 case 'l':
                 case KEY_RIGHT:
                   if (col < COLS - 1) col++;
                   else flash ();
                   break;
    
                 /* i: переход в режим ввода */
                 case KEY_IC:
                 case 'i':
                   input ();
                   break;
    
                 /* x: удалить текущий символ */
                 case KEY_DC:
                 case 'x':
                   delch ();
                   break;
    
                 /* o: вставить строку и перейти в режим ввода */
                 case KEY_IL:
                 case 'o':
                   move (++row, col = 0);
                   insertln ();
                   input ();
                   break;
    
                 /* d: удалить текущую строку */
                 case KEY_DL:
                 case 'd':
                   deleteln ();
                   break;
    
                 /* CTRL+L: перерисовать экран */
                 case KEY_CLEAR:
                 case CTRL('L'):
                   wrefresh (curscr);
                   break;
    
                 /* w: записать и закончить работу */
                 case 'w':
                   return;
    
                 /* q: закончить работу без записи файла */
                 case 'q':
                   endwin ();
                   exit (2);
    
                 default:
                   flash ();
                   break;
               }
             }
           }
    
           /* Режим ввода: принимает и вставляет символы
              Выход: CTRL+D или EIC */
           input ()
           {
             int c;
    
             standout ();
             mvaddstr (LINES - 1, COLS - 20, "Режим ввода");
             standend ();
             move (row, col);
             refresh ();
               for (;;) {
                 c = getch ();
                 if (c == CTRL('D') || c == KEY_EIC) break;
                 insch (c);
                 move (row, ++col);
                 refresh ();
               }
               move (LINES - 1, COLS - 20);
               clrtoeol ();
               move (row, col);
               refresh ();
           }
    

    5.2. Программа highlight

    Эта программа иллюстрирует использование подпрограммы attrset(). Программа highlight читает текстовый файл и использует находящиеся в нем управляющие цепочки для переключения атрибутов отображения. \U означает подчеркивание, \B - повышенную яркость, \N восстанавливает подразумеваемые значения атрибутов.

    Обратите внимание на первый вызов подпрограммы scrollok(), о которой мы еще не упоминали [см. curses(3X)]. Эта подпрограмма позволяет роллировать экран терминала, если его размер меньше размера файла. Когда программа пытается вывести что-либо ниже нижнего края экрана, scrollok() автоматически роллирует на одну строку вверх и вызывает refresh().

           /* highlight: программа, использующая последовательности
              \U, \B, \N для выделения текста на экране, позволяя
              подчеркивать их или отображать с повышенной яркостью */
    
           #include <curses.h>
    
           main (argc, argv)
           int argc;
           char **argv;
           {
             FILE *fd;
             int c, c2;
             void exit (), perror ();
    
             if (argc != 2) {
               fprintf (stderr, "usage: %s file\n", argv [0]);
               exit (1);
             }
    
             fd = fopen (argv [1], "r");
             if (fd == NULL) {
               perror (argv [1]);
               exit (2);
             }
    
             initscr ();
             scrollok (stdscr, TRUE);
             nonl ();
    
             while ((c = getc (fd)) != EOF) {
               if (c == '\\') {
                 c2 = getc (fd);
                 switch (c2) {
                   case 'B':
                     attrset (A_BOLD);
                     continue;
                   case 'U':
                     attrset (A_UNDERLINE);
                     continue;
                   case 'N':
                     attrset (0);
                     continue;
                 }
                 addch (c);
                 addch (c2);
               }
               else addch (c);
             }
    
             fclose (fd);
             refresh ();
             endwin ();
             exit (0);
           }
    

    5.3. Программа scatter

    Эта программа берет первые (LINES - 1) строк стандартного ввода и отображает символы на экране терминала в случайном порядке. Чтобы эта программа работала правильно, входной файл не должен содержать символов табуляции и неотображаемых символов.

           /* Программа scatter */
    
           #include <curses.h>
           #include <sys/types.h>
    
           extern time_t time ();
    
           #define MAXLINES 120
           #define MAXCOLS  160
    
           char s [MAXLINES] [MAXCOLS]; /* Массив экрана */
           int  T [MAXLINES] [MAXCOLS]; /* Результирующий массив,
                                чтобы сохранять количество
                                и расположение введенных символов */
    
           main ()
           {
             register int row = 0, col = 0;
             register int c;
             int char_count = 0;
             time_t t;
             void exit (), srand ();
    
             initscr ();
             for (row = 0; row < MAXLINES; row++)
               for (col = 0; col < MAXCOLS; col++)
                 s [row] [col] = ' ';
    
             col = row = 0;
    
             /* Считываем */
             while ((c=getchar ()) != EOF && row < LINES) {
               if (c != '\n') {
                 /* Помещаем символ в массив экрана */
                 s [row] [col] = c;
                 if (c != ' ') char_count++;
               }
               else {
                 col = 0;
                 row++;
               }
             }
    
             time (&t); /* Инициализация датчика случайных чисел */
             srand ((unsigned) t);
    
             while (char_count) {
               row = rand () % LINES;
               col = (rand () >> 2) % COLS;
               if (T [row] [col] !=1 && s [row] [col] != ' ') {
                 move (row, col);
                 addch (s [row] [col]);
                 T [row] [col] = 1;
                 char_count--;
                 refresh ();
               }
             }
    
             endwin ();
             exit (0);
           }
    

    5.4. Программа show

    Программа show просматривает файл, показывая его содержимое экран за экраном в ответ на нажатия пробела. Программа вызывает cbreak(), чтобы не нужно было нажимать возврат каретки после пробела, и noecho(), чтобы пробел не выводился на экран. Не обсуждавшаяся ранее подпрограмма nonl() вызывается для дополнительной оптимизации. Также не обсуждавшаяся ранее подпрограмма idlok() вызывается, чтобы дать возможность вставлять и удалять строки [дополнительную информацию см. в curses(3X)]. Заметьте, что вызываются еще подпрограммы clrtoeol(), clrtobot().

           #include <curses.h>
           #include <signal.h>
    
           main (argc, argv)
           int argc;
           char **argv;
           {
             FILE *fd;
             char linebuf[BUFSIZ];
             int line;
             void done(), perror(), exit();
    
             if (argc != 2) {
               fprintf (stderr, "Вызов: %s файл\n", argv [0]);
               exit (1);
             }
    
             fd = fopen (argv [1], "r");
             if (fd == NULL) {
               perror (argv [1]);
               exit (2);
             }
    
             signal (SIGINT, done);
    
             initscr ();
             noecho ();
             cbreak ();
             nonl ();
             idlok (stdscr, TRUE);
    
             while (1) {
               move (0, 0);
               for (line = 0; line < LINES; line++) {
                 if (!fgets (linebuf, sizeof linebuf, fd)) {
                   clrtobot ();
                   done ();
                 }
                 move (line, 0);
                 printw ("%s", linebuf);
               }
               refresh ();
               if (getch == 'q') done();
             }
           }
    
           void done ()
           {
             move (LINES - 1, 0);
             clrtoeol ();
             refresh ();
             endwin ();
             exit (0);
           }
    

    5.5. Программа two

    Эта программа просматривает входной файл, выводя первую его страницу на экран терминала, с которого она была вызвана, а вторую - на терминал, имя которого указывается в командной строке. Затем она ждет нажатия пробела на одном из терминалов, после чего выводит на него следующую страницу.

    Программа two - это простейший пример двухтерминальной curses-программы. Она не поддерживает уведомления и требует указания имени и типа второго терминала в командной строке. После этого на втором терминале нужно ввести команду sleep 100000, чтобы перевести его в неактивное состояние на время работы программы. Пользователь первого терминала должен иметь полномочия для ввода и вывода на втором терминале.

           #include <curses.h>
           #include <signal.h>
    
           SCREEN *me, *you;
           SCREEN *set_term ();
    
           FILE *fd, *fdyou;
           char linebuf [512];
    
           main (argc, argv)
           int argc;
           char **argv;
           {
             void done (), exit ();
             unsigned sleep ();
             char *getenv ();
             int c;
    
             if (argc != 4) {
               fprintf (stderr, "usage: %s oterm otype ifile\n",
                        argv [0]);
               exit (1);
             }
    
             fd = fopen (argv [3], "r");
             fdyou = fopen (argv [1], "w+");
             signal (SIGINT, done);    /* Красиво умереть */
    
             me = newterm (getenv ("TERM"), stdout, stdin);
             /* Инициализация своего терминала */
             you = newterm (argv [2], fdyou, fdyou);
             /* Инициализация второго терминала */
    
             set_term (me); /* Устанавливаем режимы своего терминала */
             noecho ();     /* Отменяем эхо */
             cbreak ();     /* Включаем cbreak */
             nonl ();       /* Разрешаем переход к новой строке */
             nodelay (stdscr, TRUE)    /* Не зависаем на вводе */
    
             set_term (you); /* Уст. реж. другого терминала */
             noecho ();      /* Отменяем эхо */
             cbreak ();      /* Включаем cbreak */
             nonl ();        /* Разрешаем переход к новой строке */
             nodelay (stdscr, TRUE)    /* Не зависаем на вводе */
    
             /* Выдаем первый экран на свой терминал */
             dump_page (me);
    
             /* Выдаем второй экран на другой терминал */
             dump_page (you);
    
             for (;;) {      /* Для каждого экрана */
               set_term (me);
               c = getch ();
               /* Ждем, пока пользователь прочитает все это */
               if (c == 'q') done ();
               if (c == ' ') dump_page (me);
               set_term (you);
               c = getch ();
               /* Ждем, пока пользователь прочитает все это */
               if (c == 'q') done ();
               if (c == ' ') dump_page (you);
               sleep(1);
             }
           }
    
           dump_page (term)
           SCREEN *term;
           {
             int line;
    
             set_term (term);
             move (0, 0);
             for (line = 0; line < LINES - 1; line++) {
               if (fgetc (linebuf, sizeof linebuf, fd) == NULL) {
                 clrtobot ();
                 done ();
               }
               mvaddstr (line, 0, linebuf);
             }
             standout ();
             mvprintw (LINES - 1, 0, "--Еще--");
             standend ();
             refresh ();     /* Выводим */
           }
    
           /* Очищаем и заканчиваем */
           void done ()
           {
             /* Очищаем первый терминал */
             set_term (me);
             move (LINES - 1, 0); /* В левый нижний угол */
    
             clrtoeol ();         /* Очищаем нижнюю строку */
             refresh ();          /* Все обновляем */
             endwin ();           /* Для выхода из curses */
    
             /* Очищаем второй терминал */
             set_term (you);
             move (LINES - 1, 0); /* В левый нижний угол */
    
             clrtoeol ();         /* Очищаем нижнюю строку */
             refresh ();          /* Все обновляем */
             endwin ();           /* Для выхода из curses */
             exit (0);
           }
    

    5.6. Программа window

    Эта программа представляет собой пример работы со многими окнами. Основная информация содержится в stdscr. Если на физический экран нужно временно вывести что-либо еще, создается новое окно, покрывающее часть экрана. Обращение к wrefresh() для этого окна приводит к тому, что его содержимое записывается на экран терминала поверх содержимого stdscr. Последующий вызов refresh() для stdscr приводит к восстановлению экрана. Обратите внимание на вызов подпрограммы touchwin() [мы не упоминали о ней ранее, см. curses(3X)], который производится перед выводом содержимого окна поверх того окна, которое уже находится на экране. Эта подпрограмма препятствует оптимизации работы с экраном. Если у Вас возникают проблемы с выводом одного окна поверх другого, для нормальной работы следует предварительно вызывать touchwin() для нового окна.

           #include <curses.h>
    
           WINDOW *cmdwin;
    
           main ()
           {
             int i, c;
             char buf [120];
             void exit ();
    
             initscr ();
             nonl ();
             noecho ();
             cbreak ();
    
             cmdwin = newwin (3, COLS, 0, 0); /* Верхние 3 строки */
             for (i = 0; i < LINES; i++)
               mvprintw (i, 0, "Это строка %d окна stdscr", i);
    
             for (;;) {
               refresh ();
               c = getch ();
               switch (c) {
                 case 'c':   /* Ввод команды с клавиатуры */
                   werase (cmdwin);
                   wprintw (cmdwin, "Введите команду:");
                   wmove (cmdwin, 2, 0);
                   for (i = 0; i < COLS; i++)
                     waddch (cmdwin, '-');
                   wmove (cmdwin, 1, 0);
                   touchwin (cmdwin);
                   wrefresh (cmdwin);
                   wgetstr (cmdwin, buf);
                   touchwin (stdscr);
    
                   /* Теперь команда находится в буфере.
                      В этом месте должны располагаться операторы
                      для ее обработки */
    
                 case 'q':
                   endwin ();
                   exit (0);
               }
             }
           }
    


    НазадОглавлениеВперед
    КаталогИндекс раздела