Date Редакция Категория comp Теги ncurses / C

Библиотека 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();
}

nchellow.png

Здесь, для наглядности, границы окна показаны с помощью функции 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();
}

ncplot.gif

Цвет

Для выводимых символов можно устанавливать цвет символа и цвет фона. Ниже приведен фрагмент программы, в которой надпись "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)

nccolor.png

Остальная часть этой программы — такая же как в примере из раздела "Экран и окна".

Перед началом использования цветов необходимо проверить, доступны ли они нам, и убедиться, что количество возможных цветовых пар 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')

ncattr.gif

Обратите внимание, что атрибуты окна относятся ко всему содержимому окна, в том числе и к рамке. В то же время надпись "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();
}

ncin.gif

Ссылки



Комментарии

comments powered by Disqus