Библиотека ncurses

  1. Благодарности
  2. Соглашения
  3. Краткое описание ncurses
  4. Знакомство
  5. Вывод
  6. Режимы вывода
  7. Ввод
  8. Режимы ввода
  9. Управление курсором
  10. Другие полезные функции
  11. Окна
  12. Панели

Благодарности

Глозштейну Александру Моисеевичу за то, что показал библиотеку ncurses.
Victor Wagner за то, что показал библиотеку PDCurses.
Отдельное спасибо E.L.K. за пример использования UTF-8 в ncurses. И конечно всем тем, кто не мешал :).

Соглашения

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

Данное руководство многократно изменялось, по тому не исключены ошибки. Обнаруженные недочеты, поправки и дополнения просьба присылать автору.

Для изучения данного материала предполагается наличие у читателя начальных знаний языка С.

текст на синем фоне обозначает исходный текст программы

текст на чёрном фоне обозначает команду, которую нужно выполнить в командной строке

Краткое описание

Библиотека ncurses (аналог библиотеки crt в Pascal) предназначена для управления вводом выводом на терминал. Она позволяет программисту не беспокоиться об аппаратных различиях терминалов и писать достаточно переносимый код. Файлы библиотеки находятся в папке /usr/include/curses/, дополнительную информацию и примеры можно найти в /usr/share/doc/ncurses-версия_библиотеки и в /usr/share/doc/ncurses-devel-версия_библиотеки.

Библиотека ncurses построена на основе библиотеки curses. Есть и другие библиотеки, построенные на базе curses. Материал представленный здесь построен на общих функциях библиотек ncurses и PDCurses для создания максимально переносимых приложений.

Основные понятия

Экран - окно максимального для данного терминала размера.

Терминал - экран, на котором располагаются окна.

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

Новые типы и константы

Типы

chtype тип символов, с которыми работает ncurses (он включает в себя код символа, цвет и дополнительные атрибуты)
bool логический тип (может быть ложным FALSE или истинным TRUE)
WINDOW структура, хранящая данные окна
SCREEN структура, хранящая данные терминала

Константы

TRUE логическая истина (1)
FALSE логическая ложь (0)

Большинство функций возвращает код ошибки.
ERR значение, возвращаемое при ошибке (-1)
OK значение, возвращаемое при отсутствии ошибки
Подробнее о том в каких случаях и что возвращает конкретная функция можно узнать в документации или в man.

Знакомство

Рассмотрим работу с ncurses на простом примере:
/*******************************************************
sample1.c - пример простой программы передвигающей
курсор в определённую позицию экрана и печатающей текст
********************************************************/

#include <ncurses.h> //подключаем библиотеку ncurses

main(int argc,char *argv[])
{
  // инициализация (должна быть выполнена 
  // перед использованием ncurses)
  initscr();
  
  // перемещение курсора в стандартном экране y=10 x=30
  move(10,30);

  printw("Hello world !!!"); // вывод строки
  refresh(); // обновить
  getch(); // ждём нажатия символа
  
  endwin(); // завершение работы с ncurses
}
Листинг 1

Что нового Вы увидели в этом примере?
1) Чтобы Вы смогли воспользоваться любой библиотекой её нужно подключить. Для библиотеки ncurses это делается добавлением "#include <ncurses.h>" в начале программы.
2) Для того чтобы начать работу с библиотекой ncurses необходимо её проинициализировать. Для этого нужно вызвать функцию initscr().
3) При завершении работы с библиотекой ncurses нужно вызвать endwin().
4) printw и getch аналогичны printf и getchar из <stdio.h>
5) библиотека ncurses позволяет ускорить вывод на экран за счёт того, что она использует запись не непосредственно на экран, а в буфер в памяти и когда нужно отобразить все изменения при помощи функции refresh() выводится только изменившаяся часть экрана.

Таким образом, каждая программа, использующая ncurses, будет иметь следующую структуру:

...
#include <ncurses.h>
...
initscr();

работа с ncurses

endwin();
...

Рассмотрим компиляцию программы использующую ncurses. Чтобы откомпилировать пример приведённый в листинге 1 нужно выполнить команду:

gcc sample1.c -o sample1 -lncurses

Если Вы уже компилировали программы в gcc, то наверно заметили, что добавился новый параметр -lncurses.

Для ввода-вывода в С используются такие функции как putchar, printf, scanf, getch. Однако их нельзя использовать вместе с библиотекой ncurses. Для ввода-вывода на экран в программах использующих ncurses могут использоваться только функции объявленные в самой библиотеке ncurses.

Вывод

Вывод символа
int addch(const chtype ch)
выводит символ ch в текущую позицию курсора и перемещает курсор на один символ вправо или в начало следующей строки, если курсор находится у правой границы экрана. (аналогична функции putchar из <stdio.h>) chtype тип символов с которыми работает ncurses (он включает в себя код символа, цвет и дополнительные атрибуты).

Вставка символа
int insch(chtype ch)
вставляет символ ch слева от курсора и все символы стоящие после курсора перемещаются на одну позицию вправо.

Вывод строки с преобразованием по формату
int printw(const char *fmt,...)
пример:

...
i=1;
printw("Значение i=%d",i);
...
выведет Значение i=1
(аналогична функции printf)

Вывод строки типа chtype*
int addchnstr(const chtype *chstr, int n)
выводит первые n символов или всю строку символов chstr, если n=-1 в позицию, где расположен курсор.

Вывод строки типа char*
int addstr(const char *str)
выводит строку str в позицию, где расположен курсор.

Вставка строки
int insnstr(const char* str, int n)
вставляет первые n символов или всю строку символов str, если n=-1 в позицию, где расположен курсор. (положение курсора не изменяется, то что стояло справа от курсора сдвигается вправо)

Вставка пустой строки
int insertln()
вставляет пустую строку (строки стоящие ниже начиная с текущей сдвигаются вниз на одну строку)

Режимы вывода

Для символов типа chtype можно устанавливать такие атрибуты, как мигание или цвет символа и фона. Для добавления символу атрибута мигания нужно включить флажок A_BLINK. Далается это так:
chtype ch = 'w' | A_BLINK;
Теперь при выводе этого символа он будет мигать, если конечно это позволяет сделать терминал. (A_DIM - пониженная яркость, A_BOLD - повышенная яркость, A_NORMAL - нормальное отображение, A_UNDERLINE - подчёркнутый, A_REVERSE - инверсный)

С включением цвета немного сложнее. Перед использованием цветов нужно проинициализировать палитру. Палитра это структура, в которой определённой цифре соответствует определённый цвет. В нашем случае одной цифре соответствуют сразу два цвета символов и фона.

...
chtype ch;
...
if (!has_colors())
{
  endwin();
  printf("Цвета не поддерживаются");
  exit(1);
}
start_color();

// 1 цвет в палитре - красные символы на чёрном фоне
init_pair(1, COLOR_RED, COLOR_BLACK);

// 2 цвет в палитре - зелёные символы на желтом фоне
init_pair(2, COLOR_GREEN, COLOR_YELLOW);

...
ch = 'w' | COLOR_PAIR(1); // символ с цветом 1 из палитры
Функция has_colors позволяет узнать можно ли использовать цвета. Функция start_color() должна вызываться до задания палитры. Функция init_pair() нужна чтобы задать какой цифре какой цвет будет соответствовать от 1 до COLOR_PAIRS-1 (0 зарезервирован для стандартного отображения). Для использования цвета в символе нужно включить флажок COLOR_PAIR(номер из палитры).
Список цветов:
COLOR_BLACK
COLOR_RED
COLOR_GREEN
COLOR_YELLOW
COLOR_BLUE
COLOR_MAGENTA
COLOR_CYAN
COLOR_WHITE

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

Включение атрибутов
int attron(int attrs)
включает атрибуты attrs. (Например attron(COLOR_PAIR(1)); устанавливает цвет 1 из палитры)

Отключение атрибутов
int attroff(int attrs)
отключает атрибуты attrs. (Например attroff(A_BLINK); отключает мигание)

Установка атрибутов
int attrset(int attrs)
Заменяет текущие атрибуты атрибутами attrs (Например attrset(A_NORMAL); заменяет текущие атрибуты на A_NORMAL)

Установка атрибутов очистки
void bkgdset(chtype ch)
Устанавливает атрибуты с которыми очищается экран такими функциями как clear(). (Например bkgdset(COLOR_PAIR(1)); очистка будет осуществляться цветом 1 из палитры)

Ввод

Ввод символа
int getch()
возвращает введённый символ (аналогична функции getchar) Если включён режим обработки командных клавиш можно узнать о нажатии функциональных клавиш и клавиш управления курсором. Константы соответствующие кодам этих клавиш можно найти в файле curses.h. Вот самые важные из них:
KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT - клавиши стрелок
KEY_F(n) Функциональные клавиши n может принимать значения от 0 до 63
KEY_BACKSPACE Backspace
KEY_DC Delete
KEY_IC Insert
KEY_HOME Home
KEY_END End
KEY_NPAGE Page Down
KEY_PPAGE Page Up

Ввод строки по формату
int scanw(char *fmt,...)
(аналогична функции scanf) Например для ввода пользователем числа в переменную i вызов функции будет выглядеть так:
scanw("%d", &i);

Режимы ввода

Автоматическое отображение при вводе
int noecho() и int echo()
Функция noecho отключает автоматическое отображение при вводе. Это нужно, если программист сам хочет решать вывоить ему полученный символ или нет. Функция echo отменяет действие функции noecho.

Установка времени ожидания
int halfdelay(int tenths)
По умолчанию такие функции как getch ждут ввода до тех пор пока пользователь не нажмёт клавишу. Функция halfdelay устанавливает режим в котором ввод ожидается tenths десятых долей секунды, затем в getch возращается ERR, если пользователь не нажимал клавиши. Отменить этот режим можно вызвав функцию nocbreak().

Обработка командных клавиш
int keypad(WINDOW *win, bool bf)
По умолчанию обработку таких клавиш, как клавиши управления курсором и функциональные клавиши берёт на себя терминал. Чтобы установить режим обработки командных клавиш нужно вызвать функцию keypad с TRUE в качестве второго параметра. Первый параметр указывает для какого окна Вы хотите установить данный режим. Если Вы не используете окна можно указать stdscr. Для отключения нужно передать FALSE во втором параметре.

Управление курсором

!!! Внимание: Все координаты в ncurses отсчитываются от верхнего левого угла и начиная с 0. Таким образом верхний левый угол имеет координаты (0,0)

Перемещение курсора
int move(int y, int x)
устанавливает курсор в позицию x,y

Получение текущих координат курсора
void getyx(WINDOW *win, int y, int x)
В переменные x,y записываются текущие координаты окна win. Если Вы не используете окна в качестве первого параметра можно указать stdscr.

Получить размеры экрана можно вызвав функции getmaxx(stdscr) и getmaxy(stdscr) они возвращают максимальное значение x и y соответственно для данного экрана.

Другие полезные функции

Очистка экрана
int clear()
заполняет весь экран пробелами

Очистка от курсора до конца строки
int clrtoeol()
заменяет пробелами интервал от курсора до конца строки

Очистка от курсора до конца экрана
int clrtobot()
заменяет пробелами интервал от курсора до конца экрана

Вставка/удаление строк
int insdelln(int n)
для положительного n вставляет n пустых строк для отрицательного удаляет n строк.

Удаление символа
int delch()
удаляет символ на котором стоит курсор (символы стоящие справа от курсора сдвигаются влево)

Удаление строки
int deleteln()
удаляет строку на которой стоит курсор (строки стоящие ниже сдвигаются вверх)

Окна

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

После инициализации создаётся окно stdscr с максимально возможными для данного терминала размерами. Если окна не будут использоваться во всех функциях требующих окно можно указывать stdscr.

Внутри одного окна также можно создать окна которые будет называться подокнами

Создание окна
WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x)
создаёт окно в котором nlines строк и ncols столбцов (если nlines или ncols равны 0 то им будет присвоено begin_y или begin_x) с координатами левого верхнего угла (begin_x,begin_y). При вызове с нулевыми аргументами ф-ия создаст окно размером с экран.

Создание подокна
WINDOW *subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x)
создаёт подокно в котором nlines строк и ncols столбцов (если nlines или ncols равны 0 то им будет присвоено begin_y или begin_x) с координатами левого верхнего угла (begin_x,begin_y) относительно всего экрана. origwin - родительское окно.

Создание подокна 2
WINDOW *derwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x)
делает то же самое что и subwin, за исключением того, что begin_x и begin_y координаты относительно окна родителя origwin.

Удаление окна/подокна
int delwin(WINDOW *win)
удаляет окно/подокно win.

Перемещение окна
int mvwin(WINDOW *win, int y, int x)
перемещает окно win в новую позицию (x,y) левого верхнего угла.

Перемещение подокна
int mvderwin(WINDOW *win, int par_y, int par_x)
перемещает подокно win в новую позицию (x,y) левого верхнего угла относительно родительского окна.

Окно играет роль ограничителя вывод на экран. При вводе-выводе в перекрывающиеся окна данные в одном окне могут быть затёрты данными выводимыми в другое окно в месте перекрытия окон. Если Вам нужно обеспечить вывод в перекрывающиеся окна так чтобы вывод в одно окно не повредил данные другого окна можно воспользоваться панелями.

Панели

Под панелью в библиотеке ncurses понимается окно к которому добавляется глубина. Все панели заносятся в стек панелей. Глубина указывает в каком порядке располагаются панели на экране. Каждую панель можно переместить вверх или вниз всех остальных панелей. Можно скрыть и показать панель.

Для использования панелей нужно подключить библиотеку панелей:
#include <panel.h>
Если вы используете panel.h можно не подключать библиотеку ncurses.h потому, что panel.h сама подключает ncurses.h.

Для компиляции программ использующих панели так же добавляется ключ -lpanel:

gcc имя_файла.c -o имя_файла -lpanel -lncurses

!!! Внимание: при компиляции с библиотеками зависящими от ncurses (в данном случае panel) ключ подключения ncurses (-lncurses) должен всегда стоять последним.

Создание панели
PANEL *new_panel(WINDOW *win)
Панель создаётся из уже созданного окна win. После создания панель перемещается в вершину стека панелей.

Удаление панели
int del_panel(PANEL *pan)
Удаляет панель pan. Окно связанное с панелью нужно удалять самостоятельно.

Получение окна связанного с панелью
WINDOW *panel_window(const PANEL *pan)
Возвращает указатель на окно связанное с панелью pan.

Обновление панелей
void update_panels()
Обновляет стек панелей. Для реального отображения на экране нужно вызвать функцию doupdate().

Скрытие панели
int hide_panel(PANEL *pan)
Удаляет панель pan из стека панелей. Таким образом при обновлении экрана мы её не увидим.

Отображение панели
int show_panel(PANEL *pan)
Делает скрытую панель pan снова видимой размещая её при этом поверх остальных панелей.

Перемещение панели поверх всех
int top_panel(PANEL *pan)
Перемещает панель в стеке поверх всех оставшихся панелей.

Перемещение панели ниже всех
int bottom_panel(PANEL *pan)
Перемещает панель в стеке ниже всех оставшихся панелей.

Перемещение панели
int move_panel(PANEL *pan, int starty, int startx)
Перемещает панель pan так, чтобы его верхний левый угол оказался в точке (startx,starty). Положение в стеке панелей не изменяется.

Замена окна
int replace_panel(PANEL *pan, WINDOW *window)
Заменяет в панели pan текущее окно на окно window. Используется для таких операций как изменение размеров панели. Положение в стеке панелей не изменяется.

Получение панели выше
PANEL *panel_above(const PANEL *pan)
Позволяет узнать какая панель находится выше панели pan. Если 0 заничит выше pan нет панелей.

Получение панели ниже
PANEL *panel_below(const PANEL *pan)
Позволяет узнать какая панель находится ниже панели pan. Если 0 заничит ниже pan нет панелей.

Установка указателя на пользовательские данные
int set_panel_userptr(PANEL *pan, const void *ptr)
В каждой панели можно установить указатель на любую структуру. Данная функция позволяет установить этот указатель ptr для панели pan.

Получение указателя на пользовательские данные
int set_panel_userptr(PANEL *pan, const void *ptr)
Данная функция позволяет получить на заданный функцией set_panel_userptr указатель ptr для панели pan.

Пример работы с панелями:

/*******************************************************
sample2.c - пример простой программы демонстрирующей
возможности панелей.
********************************************************/

#include <panel.h>

#define NLINES 10
#define NCOLS 40

void init_wins(WINDOW **wins, int n);

int main()
{	
    WINDOW *my_wins[3];
    PANEL  *my_panels[3];
    PANEL  *top;
    int ch;
    int i;

    /* инициализация ncurses */
    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);

    /* инициализация цветовой палитры */
    if (!has_colors())
    {
	endwin();
	printf("\nОшибка! Не поддерживаются цвета\n");
	return 1;
    }
    start_color();
    init_pair(1, COLOR_RED, COLOR_BLUE);
    init_pair(2, COLOR_GREEN, COLOR_RED);
    init_pair(3, COLOR_BLUE, COLOR_GREEN);

    // инициализируются окна
    init_wins(my_wins, 3);
	
    // создание панелей на основе созданных окон
    my_panels[0] = new_panel(my_wins[0]);
    my_panels[1] = new_panel(my_wins[1]);
    my_panels[2] = new_panel(my_wins[2]);

    // устанавливаем указатели на следующее окно
    // для перехода при нажатии Tab на следующее окно
    set_panel_userptr(my_panels[0], my_panels[1]);
    set_panel_userptr(my_panels[1], my_panels[2]);
    set_panel_userptr(my_panels[2], my_panels[0]);

    // обновление и прорисовка
    update_panels();
    mvprintw(0, 0, "Tab - следующая панель F1 - Выход");
    doupdate();

    // т.к. панель с индексом 2 создавалась последней 
    // значит она и будет верхней
    top = my_panels[2];

    // цикл обработки сообщений с клавиатуры
    while((ch = getch()) != KEY_F(1))
    {	
    	switch(ch)
	{
	    case '\t': // переход на следующую панель
			top = (PANEL *)panel_userptr(top);
			top_panel(top);
			break;
        }
	
	// обновление и перерисовка
	update_panels();
	doupdate();
    }

    // уничтожение созданных панелей и окон
    for(i = 0; i < 3; ++i)
    {
	del_panel(my_panels[i]);
	delwin(my_wins[i]);
    }

    //завершение программы
    endwin();
    return 0;
}

// инициализируются окна
void init_wins(WINDOW **wins, int n)
{
    int x, y, i;

    y = 2;
    x = 10;

    for(i = 0; i < n; ++i)
    {
	wins[i] = newwin(NLINES, NCOLS, y, x);
	wbkgdset(wins[i], COLOR_PAIR(i+1));
	wclear(wins[i]);
	wrefresh(wins[i]);
		
	y += 3;
	x += 7;
    }
} 
(C) Copyright 2005-2006 Бердников Александр