Библиотека ncurses обеспечивает программиста функциями для создания текстового интерфейса пользователя (Text-based User Interface — TUI) не зависящего от типа терминала. В частности, ncurses позволяет создавать окна и управлять их характеристиками, задавать экранные координаты, цвет и другие атрибуты выводимых символов. Она также оптимизирует операции по обновлению содержимого окон. ncurses можно рассматривать как аналог библиотеки crt в Turbo Pascal.
ncurses разрабатывается в рамках проекта GNU и работает на POSIX-совместимых системах. Для Windows существует PDCurses — свободная библиотека, реализующая большинство функций ncurses.
Структура программы
Программа, использующая ncurses, обычно имеет вид:
#include <ncurses.h>
...
main()
{
...
initscr();
// вызов функций ncurses
endwin();
...
}
Вызов initscr
инициализирует ncurses. Функция endwin
очищает все выделенные для ncurses ресурсы и должна вызываться после любой другой функции этой библиотеки.
Выведем на экран традиционную надпись "Hello, World!"
:
#include <ncurses.h>
int main()
{
initscr(); // Инициализация ncurses
curs_set(0); // Делаем курсор невидимым
printw("Hello, World!"); // Вывод строки Hello, World!
refresh(); // Отображение строки на реальном экране
getch(); // Ожидание пользовательского ввода
endwin(); // Завершение работы с ncurses
}
Заметим, что printw
выводит строку не напрямую на экран, а в специальный буфер, содержимое которого отображается на реальном экране после вызова refresh
.
Ожидание ввода данных getch
нужно для того, чтобы надпись не исчезла с экрана.
Экран и окна
Кроме экрана, в ncurses используются окна — буферы данных, содержащие изображение части экрана. С другой стороны, экран можно рассматривать как самое большое из возможных окон. Окна могут создаваться, уничтожаться, перемещаться, копироваться и т. п.
Для описания окна (экрана) в ncurses существует структура данных WINDOW
. Теперь мы можем описать работу функций библиотеки более подробно. Так, при вызове initscr
выделяется память под следующие переменные:
WINDOW *curscr // текущий экран (реальное окно)
WINDOW *stdscr // стандартный экран (буфер)
Если произойдет ошибка, то initscr
вернет ERR
. В противном случае возвращается указатель на stdscr
.
stdscr
— это буфер, в который выводит данные printw
и другие подобные функции, а curscr
— изображение, реально выводимое на экран. При вызове функции refresh
происходит копирование содержимого stdscr
в curscr
.
Переделаем наш пример так, чтобы надпись выводилась в окно:
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
if (!initscr())
{
fprintf(stderr, "Error initialising ncurses.\n");
exit(1);
}
WINDOW *win = newwin(LINES, COLS, 0, 0);
printw("Hello, world!");
curs_set(0);
refresh();
getch();
delwin(win);
endwin();
}
В начале программы мы проверяем инициализацию ncurses, а затем создаем окно с помощью функции newwin
:
WINDOW *newwin(nlines, ncols, y0, x0)
nlines
— это число строк;ncols
— число столбцов окна;y0
иx0
— координаты верхнего левого угла окна.
При этом начало координат (0,0)
находится в левом верхнем углу экрана. Переменные LINES
и COLS
инициализируются при вызове initscr
и задают высоту экрана в строках и ширину в столбцах. В нашем случае окно совпадает с экраном. Все указанные переменные имеют тип int
.
Функция delwin(win)
удаляет окно win
.
Создадим теперь окно размером 20 строк на 50 столбцов и выведем надпись в его центре:
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
const int width = 50;
const int height = 20;
if (!initscr())
{
fprintf(stderr, "Error initialising ncurses.\n");
exit(1);
}
initscr();
curs_set(0);
refresh();
int offsetx = (COLS - width) / 2;
int offsety = (LINES - height) / 2;
WINDOW *win = newwin(height, width, offsety, offsetx);
char hello[] = "Hello, world!";
mvaddstr(LINES/2, (COLS-strlen(hello))/2, hello);
box(win, 0, 0);
wrefresh(win);
getch();
delwin(win);
endwin();
}
Здесь, для наглядности, границы окна показаны с помощью функции box
.
Функции ncurses, начинающиеся с w
, предназначены для работы с окнами WINDOW
, тогда как остальные имеют дело с stdscr
. В нашем случае wrefresh(win)
обновляет curscr
, помещая в него содержимое окна win
, то есть делает с win
то же самое, что refresh
делает с stdscr
.
Функции, начинающиеся с mv
, сначала переместят курсор в позицию (y,x)
, а затем выполняют какое-то действие. Так, в примере, функция mvaddstr
перемещает курсор в середину экрана, учитывая при этом длину строки hello
, после чего выводит эту строку.
Символы
В следующем примере с помощью функции mvaddch
, выводящей заданный пользователем символ, строится график функции y = x^2
для целочисленных x
, лежащих в диапазоне [-4;4]:
#include <ncurses.h>
#include <unistd.h>
int main()
{
const int max_x = 4;
const int max_y = max_x*max_x;
initscr();
curs_set(0);
for (int x = 0; x < 2*max_x+1; x++)
{
int y = (x-max_x)*(x-max_x);
mvaddch(max_y-y,x,'o');
refresh();
sleep(1);
}
getch();
endwin();
}
Цвет
Для выводимых символов можно устанавливать цвет символа и цвет фона. Ниже приведен фрагмент программы, в которой надпись "Hello, world!"
выводится разными цветами символов и фона:
WINDOW *win = newwin(height, width, offsety, offsetx);
start_color(); // Инициализация цветов
char hello[] = "Hello, world!";
mvaddstr(5, (COLS-strlen(hello))/2, hello);
if (has_colors() && COLOR_PAIRS >= 13)
{
int n = 1;
// Инициализация цветовых пар
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
init_pair(6, COLOR_CYAN, COLOR_BLACK);
init_pair(7, COLOR_BLUE, COLOR_WHITE);
init_pair(8, COLOR_WHITE, COLOR_RED);
init_pair(9, COLOR_BLACK, COLOR_GREEN);
init_pair(10, COLOR_BLUE, COLOR_YELLOW);
init_pair(11, COLOR_WHITE, COLOR_BLUE);
init_pair(12, COLOR_WHITE, COLOR_MAGENTA);
init_pair(13, COLOR_BLACK, COLOR_CYAN);
while (n <= 13)
{
color_set(n, NULL);
mvaddstr(5 + n, (COLS-strlen(hello))/2, hello);
n++;
}
}
box(win, 0, 0);
Чтобы символ 'X'
был выведен с цветами из цветовой пары номер 4, укажем:
waddch(win, 'X' | A_UNDERLINE | COLOR_PAIR(4));
Установить атрибуты для окна можно с помощью функции wattron
:
int wattron(WINDOW *win, int attr);
которая задает для окна атрибут (или комбинацию атрибутов) attr
. После этого любой текст, выводимый в этом окне с помощью waddch
или waddstr
будет иметь атрибуты attr
.
Например, для вывода на экран сообщения в наиболее ярком режиме, наберем:
attron(A_STANDOUT);
addstr("I am highlighted!\n");
Предопределенные значения атрибутов приведены в таблице:
Атрибут | Описание |
---|---|
A_NORMAL | нормальный, переустановка всего остального |
A_STANDOUT | наиболее яркий режим |
A_UNDERLINE | подчеркивание |
A_REVERSE | обратное изображение |
A_BLINK | мигание |
A_DIM | тусклый или полуяркий режим |
A_BOLD | жирный шрифт |
A_ALTCHARSET | использование альтернативной символьной таблицы |
A_INVIS | невидимый режим |
A_PROTECT | режим защиты |
A_CHARTEXT | маска для действующих символов (chtype) |
A_COLOR | маска для цвета |
COLOR_PAIR(n) | установка цветовой пары n |
PAIR_NUMBER(a) | получение цветовой пары, лежащей в атрибуте a |
Определим для окна атрибуты "мерцание" и "жирный шрифт" и будем выводить символы с заданными ранее цветами
WINDOW *win = newwin(height, width, offsety, offsetx);
wattron(win, A_BLINK | A_BOLD);
...
while (n <= 13)
{
wmove(win, (height-13)/2 + n, width/2);
waddch(win, 'Z' | COLOR_PAIR(n));
n++;
}
[Код полностью]({filename}/images/comp/Comp.NcursesTutorial/nccolor.c]({filename}/images/comp/Comp.NcursesTutorial/ncattrr.c)
Остальная часть этой программы — такая же как в примере из раздела "Экран и окна".
Перед началом использования цветов необходимо проверить, доступны ли они нам, и убедиться, что количество возможных цветовых пар COLOR_PAIRS
не меньше того, которое мы хотим создать.
Для инициализации операций с цветом вызывается функция start_color
.
Функция has_colors
позволяет узнать, можно ли использовать цвета. Если она возвращает FALSE
, то использовать цвета нельзя.
Для создания цветовых пар служит функция init_pair
:
init_pair(pair_number, foreground, background);
foreground
— цвет символа; background
— цвет фона. pair_number
— номер созданной цветовой пары. Каждой паре будет соответствовать номер от 1 до COLOR_PAIRS-1
(0 зарезервирован для стандартного отображения). Для изменения цвета вызывается функция color_set
, в которой указывается номер нужной цветовой пары.
Атрибуты
Помимо цветов можно управлять и другими атрибутами символов, такими как тип шрифта (жирный, подчеркнутый), мерцание и т. п. Эти атрибуты представляются целыми числами, каждый бит которых отвечает за определенный видеоэффект.
Есть два способа использования атрибутов: указывать их для каждого выводимого символа или глобально — для всего окна.
Атрибут символа устанавливается при вызове функции waddch(win, ch)
или addch(ch)
. Для связывания символа с атрибутом и комбинирования атрибутов служит логическое ИЛИ. Например, A_UNDERLINE
— предопределенный атрибут, отвечающий за подчеркивание символа. Для того, чтобы вывести в окно win
символ 'X'
с подчеркиванием нужно указать:
waddch(win, 'X')
Обратите внимание, что атрибуты окна относятся ко всему содержимому окна, в том числе и к рамке. В то же время надпись "Hello, world!"
не мерцает, поскольку выводится не в окне, а на экране, на который не распространяется назначение атрибутов с помощью wattron
.
Ввод
Для чтения символа из stdscr
используется функция getch
:
int ch = getch();
По умолчанию введенный символ отображается на экране. Запретить отображение можно функцией noecho
.
Вызов функции cbreak
позволяет getch
считывать поступившие от пользователя данные непосредственно после нажатия клавиши. В противном случае полученные данные поступят в обработку только после того, как будет считан символ новой строки. Тогда данные поступают в getch
посимвольно, по принципу FIFO, до тех пор пока буфер ввода не опустеет.
По умолчанию, getch
ожидает нажатие клавиши. Это полезно, например, когда нужно притормозить выполнение программы. Отменить это умолчание можно с помощью функции nodelay(stdscr, TRUE)
. Такое поведение используются для создания циклов в игровых программах, где требуется оперативная реакция пользователя. Например:
int ch;
nodelay(stdscr, TRUE);
for (;;) {
if ((ch = getch()) == ERR) {
// пользователь не отвечает
...
}
else {
// пользователь нажал клавишу
...
}
}
Вызов функции keypad(stdstr, TRUE)
, позволяет обрабатывать нажатия специальных клавиш, таких как стрелки, Ctrl или Delete, а также их комбинаций. В результате нажатия генерируется целое число, которое можно считать, обработать и выполнить какое-либо действие.
В таблице ниже приведены коды клавиш:
Код клавиши | Описание |
---|---|
KEY_DOWN | Клавиши со стрелками |
KEY_UP | |
KEY_LEFT | |
KEY_RIGHT | |
KEY_HOME | Home |
KEY_BACKSPACE | Backspace |
KEY_F(n) | Функциональная клавиша с 0 <= n >= 63 |
KEY_DC | Delete |
KEY_IC | Insert |
KEY_ENTER | Enter |
Считывание и обработка нажатия клавиш выполняется так:
int ch = getch();
switch (ch) {
case KEY_BACKSPACE: // нажат 'Backspace'
...
case KEY_UP: // нажата 'стрелка вверх'
...
case KEY_DOWN: // нажата 'стрелка вниз'
...
case 'A' .... // нажата 'A'
...
}
Чтение символов из окна выполняется функцией
int wgetch(WINDOW *win);
Чтение может выполняется вместе с перемещением курсора в заданную позицию:
int mvgetch(int y, int x);
int mvwgetch(WINDOW *win, int y, int x);
В следующем пример символ 'X' перемещается по экрану клавишами со стрелками:
#include <ncurses.h>
int main()
{
initscr();
curs_set(0);
noecho();
keypad(stdscr, TRUE);
int x = 5;
int y = 5;
for (;;)
{
mvprintw(y, x, "X");
int ch = getch();
if (ch == KEY_DOWN) y++;
if (ch == KEY_UP) y--;
if (ch == KEY_LEFT) x--;
if (ch == KEY_RIGHT) x++;
clear();
refresh();
}
endwin();
}
Ссылки
- Мэтью Н., Стоунс Р. Основы программирования в Linux. — СПб.: БХВ-Петербург, 2009, Глава 6. — с. 253—301. Ncurses Programming Guide by by X. Li. Еще одно краткое руководство. NCURSES Programming HOWTO, Pradeep Padala. Все, что нужно. *Руководство программиста для Linux, перевод А. Паутнова. Справочник на русском.
Комментарии
comments powered by Disqus