Журнал LinuxFormat - перейти на главную

LXF91:Unix API

Материал из Linuxformat
Версия от 15:19, 1 декабря 2008; Crazy Rebel (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск
Unix API Настоящее программирование для Unix – без прикрас и библиотек-«оберток»

ncurses: привет Окнам!

ЧАСТЬ 11 Оконный интерфейс не обязательно должен быть графическим! Андрей Боровский покажет, как создавать удобные приложения, работающие прямо в консоли.
As honour, love, obedience, troops of friends,
I must not look to have; but, in their stead,
Curses, not loud but deep, mouth-honour, breath,
Which the poor heart would fain deny, and dare not.
William Shakespeare, The Tragedy of Macbeth.

В прошлый раз мы научились управлять текстовой консолью с помощью интерфейса termios. Однако, для того, чтобы представить текстовый экран во всем великолепии, возможностей termios недостаточно. Сегодня мы поговорим о дополнительном средстве управления терминалом – библиотеке ncurses. Она и вправду заставляет терминал переливаться всеми цветами радуги (вот почему во всей серии статей из серии Unix API, эта – единственная, в которой вы найдете снимки экранов).

Когда-то давным-давно графические терминалы были редкостью, а пользователи текстовых терминалов хотели работать с интерфейсами, похожими на графические (и, самое главное, использовать новое удобное средство ввода – мышь). Специально для того, чтобы предоставить интерфейс «покажи и щелкни» пользователям текстовыхm терминалов, была разработана библиотека curses (ее название происодит от ее важнейшей функции – управления курсором, а вовсе не от проклятия, которое она накладывает на программистов). Изначально библиотека curses создавалась для BSD UNIX. В Linux используется открытый (распространяющийся на условиях MIT License) клон curses – библиотека ncurses (new curses). Приложений, использующих ncurses в современной Linux-системе не так уж и много. Среди наиболее популярных проектов на базе ncurses можно назвать Midnight Commander, текстовый Web-браузер lynx, программу для чтения новостей tin и почтовый клиент mutt. Сравнительно невысокая популярность ncurses объясняется тем, что ниша ее применения сократилась. Большинство современных компьютеров поддерживают растровую графику, так что если вам нужно реализовать интерфейс «покажи и щелкни», вы, как правило, можете воспользоваться более совершенными графическими средствами. Выбирать ncurses как платформу для нового проекта следует, только если, с одной стороны, вашей программе совершенно необходим интерфейс «покажи и щелкни», а с другой – она должна работать в условиях полного отсутствия графической подсистемы.

Введение в ncurses

Основными понятиями в ncurses являются экран (screen), окно (window) и подокно (subwindow). Экраном называется все пространство, на котором ncurses может выводить данные. С точки зрения ncurses, экран – это матрица ячеек, в которые можно выводить символы. Если монитор работает в текстовом режиме, экран ncurses совпадает с экраном монитора. Если терминал эмулируется графической программой, экраном является рабочая область окна этой программы. Окном ncurses называется прямоугольная часть экрана, для которой определены особые параметры вывода. В частности, размеры окна влияют на перенос и прокрутку выводимых строк. В каком-то смысле окно можно назвать «экраном в экране». В процессе инициализации ncurses автоматически создается окно stdscr, размеры которого совпадают с размерами экрана. Кроме структуры stdscr' по умолчанию создается еще одна структура – curscr. Если окно stdscr предназначено в ncurses для стандартного вывода данных, то curscr содержит копию данных, отражающую текущее состояние экрана. Кода вы записываете данные в stdscr (или другое окно), эти данные не отображаются на экране монитора автоматически. Для того, чтобы сделать новый вывод видимым, вы должны вызывать специальную функцию обновления экрана (refresh() или wrefresh()). Эта функция сравнивает содержимое окна stdscr и curscr и обновляет экран на основе различий между ними, а затем вносит изменения в структуру curscr. Благодаря наличию окна curscr, приложению ncurses не требуется «помнить» весь свой предыдущий вывод и перерисовывать его всякий раз, когда в этом возникает потребность. Этим программы ncurses отличаются от графических программ. В старину, когда терминалы связывались с компьютерами через модемы, использование двух окон давало дополнительное преимущество в скорости обмена данными, ведь программе нужно было передавать на терминал не копию экрана целиком, а только последние изменения.

Помимо стандартных окон ncurses, вы можете создавать собственные окна размера, меньшего stdscr. Ваша программа может работать с несколькими окнами одновременно, выполняя вывод в каждое из них. Кроме окон (windows), программы ncurses могут создавать подокна (subwindows), поведение которых несколько отличается от поведения стандартных окон.

Важнейшей особенностью ncurses является возможность указать произвольную позицию курсора для вывода (и ввода) данных. Позиция курсора отсчитывается от левого верхнего угла текущего окна. Ячейка в верхнем левом углу имеет координаты (0, 0). При работе с функциями ncurses важно помнить, что первой координатой является номер строки (что соответствует y в терминах графического программирования), а второй координатой – номер столбца (соответствует x в графическом режиме).

В случае ошибки функции ncurses обычно возвращают константу ERR. Если функция не должна возвращать какое-то информативное значение (как, например, функция getch()), в случае успешного выполнения она возвращает константу OK.

Первая программа ncurses

Написание первой программы ncurses (она называется cursed, исходный текст вы найдете на DVD в файле cursed.c) мы начнем с перечисления заголовочных файлов.

 #include <termios.h>
 #include <sys/ioctl.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <curses.h>

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

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

Когда размеры окна терминала меняются, выполняющаяся в нем программа получает сигнал SIGWINCH. Это одновременно и хорошо, и плохо. Хорошо – потому, что терминал информирует программу об изменении своих размеров, плохо – потому, что сигналы имеют особенность вмешиваться в работу программы. Например, если вы напишете программу, использующую ncurses, и не позаботитесь об обработке сигнала SIGWINCH, при изменении размеров окна терминала ваша программа может неожиданно завершиться, оставив терминал в ненормальном состоянии. Давайте посмотрим, как обрабатывается сигнал SIGWINCH в программе cursed.

 void sig_winch(int signo)
 {
    struct winsize size;
    ioctl(fileno(stdout), TIOCGWINSZ, (char *) &size);
    resizeterm(size.ws_row, size.ws_col);
 }

Функция sig_winch() представляет собой обработчик сигнала SIGWINCH. Следует отметить, что изменение размеров экрана во время работы программы ncurses представляет собой довольно нетривиальную задачу и стандартного рецепта, описывающего, что нужно делать, не существует. Разработчики ncurses, как могли, постарались упростить решение этой задачи для программистов, введя функцию resizeterm(). Функцию resizeterm() следует вызывать сразу после изменения размеров окна терминала. Аргументами функции должны быть новые размеры экрана, заданные в строках и столбцах. Функция resizeterm() старается сохранить внешний вид и порядок работы приложения в окне терминала с новыми размерами, но это ей удается не всегда, с чем мы и столкнемся ниже. Необходимые для resizeterm() значения размеров окна мы получаем с помощью специального вызова ioctl(). При этом первым параметром функции ioctl() должен быть дескриптор файла устройства, представляющего терминал. Вторым параметром ioctl() должна быть константа TIOCGWINSZ, а третьим – адрес структуры struct winsize. Структура winsize, определенная в файле <sys/ioctl.h>, включает в себя поля ws_row и ws_col, в которых возвращается число строк и столбцов окна терминала.

Перейдем теперь к функции main() программы cursed:

 int main(int argc, char ** argv)
 {
    initscr();
    signal(SIGWINCH, sig_winch);
    noecho();
    curs_set(0);
    attron(A_BOLD);
    move(5, 15);
    printw(“Hello, brave new curses world!\n”);
    attroff(A_BOLD);
    attron(A_BLINK);
    move(7, 16);
    printw(“Press any key to continue...”);
    refresh();
    getch();
    endwin();
    exit(EXIT_SUCCESS);
 }

Работа с ncurses начинается с вызова функции initscr(). Эта функции инициализирует структуры данных ncurses и переводит терминал в нужный режим. По окончании работы с ncurses следует вызвать функцию endwin(), которая восстанавливает то состояние, в котором терминал находился до инициализации ncurses.

После вызова initscr() мы устанавливаем обработчик сигнал SIGWINCH. Устанавливать обработчик SIGWINCH следует именно после инициализации ncurses, поскольку в нем используется функция resizeterm(), предполагающая, что библиотека ncurses уже инициализирована. Функция noecho() отключает отображение символов, вводимых с клавиатуры. Функция cur_set() управляет видимостью курсора. Если вызвать эту функцию с параметром 0, курсор станет невидимым, вызов же функции с ненулевым параметром снова «включит» его.

Функция attron() позволяет указать некоторые дополнительные атрибуты выводимого текста. Ей можно передать одну или несколько констант, обозначающих атрибуты (в последнем случае их следует объ- единить с помощью операции «|»). Например, атрибут A_UNDERLINE включает подчеркивание текста, атрибут A_REVERSE меняет местами цвет фона и текста, атрибут A_BLINK делает текст мигающим, атрибут A_DIM снижает яркость текста по сравнению с нормальной, атрибут A_BOLD делает текст жирным в монохромном режиме и управляет яркостью цвета в цветном режиме работы монитора. Специальный атрибут COLOR_PAIR() применяется для установки цветов фона и текста. На man-странице, посвященной функции attron(), вы найдете описания и других атрибутов. Все перечисленные выше атрибуты оказывают воздействие только на тот текст, который выводится после их установки.

Сбросить атрибуты можно с помощью функции attroff(). Так же, как и в случае с attron(), функции attroff() можно передать несколько

Персональные инструменты
купить
подписаться
Яндекс.Метрика