BearLibTerminal

Материал из Клуб любителей рогаликов
Перейти к: навигация, поиск

BearLibTerminal — это небольшая (в интерфейсном плане) библиотека для организации терминал-подобного окна, вывода текста, обработки простого ввода.

Большое количество roguelike совершенно осознанно используют аскетичное символьное/псевдографическое оформление. Однако, использование стандартных средств вывода (командной строки ОС) сопряжено с досадными ограничениями скорости вывода, цветовой гаммы, используемого шрифта. Нетривиальной задачей оказывается и применение расширенного набора символов, например нескольких языков и псевдографики. BearLibTerminal позволяет обойти упомянутые ограничения оставаясь в рамках простой концепции "терминала": пользователю предоставляется прямоугольная сетка ячеек-знакомест, средства для вывода текста и чтения ввода с клавиатуры.

Достойными внимания особенностями терминала являются:

  • Высокая скорость вывода (в основе лежит OpenGL).
  • Возможность использования тайловых (в виде картинки) и векторных (TrueType) шрифтов.
  • Легкость использования Unicode.
  • Полная TrueColor палитра.
  • Возможность комбинации нескольких символов в одном знакоместе.
  • Поддержка ввода с клавиатуры и мыши.

BearLibTerminal не является:

  • Фреймворком roguelike: в библиотеке нет и никогда не будет генераторов случайных чисел, уровней или имен персонажей, механизмов расчета FOV/LOS и освещения, средств для работы с файлами или сетью.
  • Графическим движком общего назначения: функциональность библиотеки сознательно ограничивается функциональностью псевдотерминала.

Похожие инструменты: Tinycurses, libtcod.

Интерфейс библиотеки

BearLibTerminal оформлен в виде динамически подключаемой библиотеки (.dll). Используемое соглашение вызова — cdecl.

terminal_open

<syntaxhighlight lang="cpp"> int terminal_open(int codepage); </syntaxhighlight> Для начала работы с библиотекой необходимо вызвать функцию terminal_open. Эта функция принимает один параметр -- номер кодировки, которую использует приложение. Подробно о кодировках и их использовании в BearLibTerminal [будет] рассказано в отдельном параграфе. В большинстве случаев подойдет значение по умолчанию, CODEPAGE_DEFAULT. До вызова terminal_open все остальные функции библиотеки мгновенно завершаются, не выполняя никаких действий. Вызов terminal_open не приводит к немедленному выводу окна терминала на экран, окно отображается в момент первого вызова функции #terminal_refresh.

terminal_close

<syntaxhighlight lang="cpp"> void terminal_close(); </syntaxhighlight> Симметричный terminal_open вызов, закрывающий терминал и освобождающий используемые библиотекой ресурсы. Поведение программы, не вызвавшей terminal_open перед своим завершением, неопределено.

terminal_options

<syntaxhighlight lang="cpp"> // ANSI/UTF-8 int terminal_options(const char* options, ...); int terminal_options_noformat(const char* options);

// UTF-16 int terminal_woptions(const wchar_t* options, ...); int terminal_woptions_noformat(const wchar_t* options); </syntaxhighlight> Данная функция представляет собой универсальный способ задания разнообразных настроек библиотеки. Опции задаются в виде строки с парами параметр-значение, разделенными точкой с запятой:
option1=value1; option2=value2; ...

В библиотеке имеются два варианта функции, с суффиксом "noformat" и без. Вариант без суффикса (с переменным числом аргументов) позволяет использовать в С/С++ привычное форматирование передаваемой строки: <syntaxhighlight lang="cpp"> terminal_options("size=%dx%d", width, height); </syntaxhighlight> Вариант с суффиксом "noformat" не производит подобного форматирования и может быть использован при вызове функций из программы, написанной на других языках программирования, предоставляющих собственные механизмы форматирования строк.

Перечень доступных параметров

Наименование Значение по умолчанию Описание
Настройки окна
size 80x25 Размер окна терминала в знакоместах, в формате ШИРИНАxВЫСОТА.
title BearLibTerminal Заголовок окна терминала.
icon app.ico Имя .ico-файла с иконкой, которая будет использована для окна.
Настройки шрифта
font.name default Имя файла шрифта, векторного .ttf или тайлового .bmp/.png
font.size 10 Размер шрифта: высота символа для векторного шрифта, размер одного тайла для тайлового шрифта.
font.mode normal Режим растеризации при использовании векторного шрифта: monochrome, normal или lcd.
font.codepage utf-8 Кодовая страница тайлового шрифта.
Настройки ввода
input.events keypress Фильтр событий ввода, комбинация из флагов none, keypress, keyrelease, mousemove, mousescroll, all.
input.timeout infinite Таймаут ожидания события ввода: infinite, immediate или численное значение в миллисекундах.
input.precise_mousemove false Флаг, указывающий, будет ли считаться движением мыши попиксельное (true) или познакоместное (false) перемещение указателя.
input.cursor_symbol 95 (ASCII 0x5F, подчеркивание) Код символа, используемого в качестве курсора в terminal_get_str.
input.cursor_blink_rate 500 Скорость мерцания курсора в terminal_get_str, в миллисекундах.
Настройки вывода
output.postformat true Флаг, включающий или выключающий постформатирование при выводе текста.
Настройки логгирования
log.file bearlibterminal.log Имя файла, куда в библиотека будет писать свой лог. Файл не ротируется, логгирование попросту производится в конец указанного файла.
log.level error Уровень важности логгирования: none, fatal, error, warning, info, debug, trace

Настройки окна

<подробно про настройки окна>

Настройки шрифта

<подробно про настройки шрифта>

Настройки ввода

<подробно про настройки ввода>

terminal_put

<syntaxhighlight lang="cpp"> void terminal_put(int x, int y, int code); </syntaxhighlight> Одна из основных функций библиотеки, позволяет вывести символ в знакоместо с указанными координатами.

Диапазон возможных для вывода кодов символов не ограничен выбранной при вызове terminal_open кодировкой (см. #Кодировки).

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

terminal_color

<syntaxhighlight lang="cpp"> void terminal_color(color_t color); </syntaxhighlight> Устанавливает основной цвет выводимого текста. Параметром функции является целое беззнаковое 32-битное число, представляющее цвет в формате ARGB, например 0xFFFF0000 -- сплошной красный, 0x800000FF -- полупрозрачный синий.

См. #color_from_argb, #color_from_name, #Постформатирование.

terminal_bkcolor

<syntaxhighlight lang="cpp"> void terminal_bkcolor(color_t color); </syntaxhighlight> Аналогичная terminal_color функция, но устанавливающая цвет фона выводимых символов.

В зависимости от режима смешения, цвет фона может применяется к знакоместу только в случае, если символ является первым в знакоместе (см. #terminal_blending).

terminal_blending

<syntaxhighlight lang="cpp"> void terminal_blending(int mode); </syntaxhighlight> Функция устанавливает режим смешения для вывода символов: BLENDING_NONE или BLENDING_ADDITIVE.

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

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

terminal_printf

<syntaxhighlight lang="cpp"> // ANSI/UTF-8 size_t terminal_printf(int x, int y, const char* format, ...); size_t terminal_printf_noformat(int x, int y, const char* s);

// UTF-16 size_t terminal_wprintf(int x, int y, const wchar_t* format, ...); size_t terminal_wprintf_noformat(int x, int y, const wchar_t* s); </syntaxhighlight> ...

terminal_clear

<syntaxhighlight lang="cpp"> void terminal_clear(); </syntaxhighlight> Функция очистки экрана. Содержимое всех ячеек-знакомест удаляется, цвет фона устанавливается в выбранный посредством #terminal_bkcolor цвет.

terminal_clear_area

<syntaxhighlight lang="cpp"> void terminal_clear_area(int x, int y, int w, int h); </syntaxhighlight> Функция частичной очистки экрана. Содержимое ячеек-знакомест в указанной области экрана удаляется, цвет фона устанавливается в выбранный посредством #terminal_bkcolor цвет.

terminal_refresh

<syntaxhighlight lang="cpp"> void terminal_refresh(); </syntaxhighlight> Функция обновления экрана. Эта функция должна быть вызвана, чтобы изменения содержимого терминала на самом деле отобразились на экране.

Окно терминала инициализируется и выводится на экран при первом вызове terminal_refresh.

terminal_has_input

<syntaxhighlight lang="cpp"> int terminal_has_input(); </syntaxhighlight> Позволяет узнать, есть ли события в очереди ввода. Возвращает 0, если очередь пуста или 1, если есть непрочитанные события.

См. #Ввод

terminal_get

<syntaxhighlight lang="cpp"> int terminal_get(); </syntaxhighlight> Возвращает код следующего событие из очереди ввода.

Если очередь ввода пуста, поведение зависит от параметра input.timeout:

  • infinite: выполнение программы блокируется до поступления следующего события ввода.
  • N (где N -- некоторое неотрицательное значение): выполнение программы блокируется до поступления события ввода, но не более чем на N миллисекунд, по истечении которых возвращается -1.

Если input.timeout равен нулю, блокирования выполнения программы не производится, в случае пустой очереди ввода функция немедленно возвращает -1.

См. #Ввод

terminal_get_str

<syntaxhighlight lang="cpp"> int terminal_get_str(int x, int y, char* buffer, int max); // ANSI/UTF-8 int terminal_get_wstr(int x, int y, wchar_t* buffer, int max); // UTF-16 </syntaxhighlight> Функция чтения текста с клавиатуры с учетом локалей и раскладок пользователя.

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

TODO: картинка-иллюстрация

Чтение очереди ввода функцией terminal_get_str производится до:

  • Подтверждения ввода (поступления события VK_RETURN), функция возвращает количество введенных символов (может быть и 0).
  • Отмены ввода (поступления события VK_ESCAPE или VK_CLOSE), функция возвращает -2 (TERMINAL_INPUT_CANCELLED).
  • Истечения отведенного промежутка времени (таймаута), функция возвращает -1 (TERMINAL_INPUT_TIMED_OUT).

Величина таймаута, в течении которого производится ввод и в течении которого вводимая строка отображается на экране зависит от значения параметра input.timeout:

  • infinite: неограниченно, до поступления событий VK_RETURN, VK_ESCAPE или VK_CLOSE.
  • N (где N -- некоторое неотрицательное значение): N миллисекунд.

Если input.timeout равен нулю, блокирования ввода и отображения вводимой строки на экране не производится, функция обрабатывает доступные сообщения в очереди ввода и немедленно завершается, возвращая одно из перечисленных выше значений.

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

Иначе говоря, при значении input.timeout = infinite (значение по умолчанию), функция terminal_get_str производит чтение строки "до победного конца": до подтверждения или отмены ввода, полностью забирая на себя ответственность по отображению вводимого текста на экране. Пользователю библиотеки остается лишь проверить возвращенное значение и, в случае успешного завершения, использовать готовую строку в буфере buffer.

Значение input.timeout, равное или большее нуля, позволяет организовать "неблокирующий" ввод строки. Результат выполнения terminal_getstr появляется на экране на N миллисекунд, по истечении которых можно быстро обновить анимацию и продожить ввод дальше -- и так до тех пор, пока функция не вернет неотрицатильное значение (пользователь завершил ввод) или -2 (пользователь отменил ввод).

Размер передаваемого в функцию буфера должен быть достаточно большим, чтобы вместить максимально возможную вводимую строку. Так как параметр max указывается в символах, это означает, что для вызова в кодировке UTF-8 (terminal_get_str) буфер должен быть размером не менее max*3+1 (каждый символ может занять до трех байт + нуль-терминатор), а для UTF-16 вызова (terminal_get_wstr) -- не менее max+1.

См. #Ввод

terminal_state

<syntaxhighlight lang="cpp"> int terminal_state(int code); </syntaxhighlight> Данная функция позволяет получить текущее состояние некоторых переменных терминала. Код переменной берется из перечисления VK_xxx.

  • Состояние клавиш: коды VK_ESCAPE, VK_0, VK_NUMPAD0, VK_A и т. д. Функция возвращает 1, если клавиша нажата или 0, если отпущена.
  • Состояние мыши: VK_MOUSE_X и VK_MOUSE_Y (в знакоместах), VK_MOUSE_PIXEL_X, VK_MOUSE_PIXEL_Y (в пикселях), VK_MOUSE_WHEEL (в шагах колесика).
  • Размеры знакоместа: VK_CELL_WIDTH и VK_CELL_HEIGHT (в пикселях).

В зависимости от выбранного фильтра событий ввода (input.events), состояние клавиш и мыши возвращается не на момент вызова, а соответствующее текущему состоянию очереди ввода. Обновление значений, возвращаемых функцией terminal_state, производится по мере выбора соответствующих событий из очереди ввода посредством terminal_get.

Например, если пользователь нажмет сочетание клавиш Shift+Z, в очереди ввода оно будет представлено как Shift(нажато), Z(нажато), Z(отпущено), Shift(отпущено). На момент выбора из очереди ввода события о нажатии клавиши Z, состояние клавиши Shift будет точно "нажато", даже если обработка ввода будет произведена несвоевременно и на самом деле клавиша Shift к тому моменту будет уже отпущена. Это позволяет производить проверку нажатия сочетаний клавиш элементарным образом: <syntaxhighlight lang="cpp"> int code = terminal_get();

if (code == VK_Z) {

   if (terminal_state(VK_SHIFT))
   {
       // Нажато Shift+Z
   }
   else if (terminal_state(VK_CTRL))
   {
       // Нажато Ctrl+Z
   }
   else
   {
       // Нажата просто Z
   }

} </syntaxhighlight> Однако, если событие не попадает под фильтр событий ввода (input.events), оно не может быть выбрано из очереди посредством terminal_get и будет обработано автоматически. Если фильтр равен none, то все события обрабатываются автоматически и terminal_state всегда возвращает текущее состояние.

terminal_use_image

<syntaxhighlight lang="cpp"> int terminal_use_image(int code, const char* file); // ANSI/UTF-8 int terminal_use_wimage(int code, const wchar_t* file); // UTF-16 </syntaxhighlight> Функция, позволяющая загрузить и использовать в качестве спрайта произвольное изображение. В параметре code указывается код "символа", с которым будет ассоциировано изображение, а в параметре -- имя файла с изображением. Допускается загрузка из PNG, BMP и JPEG. Загруженное изображение выводится на экране вместо символа с указанным кодом: <syntaxhighlight lang="cpp"> terminal_use_image(0xE000, "tile1.png"); terminal_put(10, 10, 0xE000); </syntaxhighlight> В приведенном случае вместо символа с кодом 0xE000 (которого в шрифте все равно нет, так как это код из специально оставленной пустой области Unicode), на экране начиная с позиции (10, 10) будет отрисована картинка из файла "tile1.png".

Следует учитывать порядок отрисовки. Все клетки в BearLibTerminal отрисовываются строго построчно, слева направо, строки сверху вниз. Обычно символы вписываются в границы клеток, не пересекаются и порядок вывода не имеет значения. Однако это может играть роль при выводе спрайтов: <syntaxhighlight lang="cpp"> terminal_use_image(0xE001, "tile2.png"); terminal_printf(20, 20, "a[uE001]b"); </syntaxhighlight> Допустим, что картинка-спрайт из "tile2.png" имеет размер больше одного знакоместа. Тогда в 20 строке сначала будет выведен символ "a" (20 колонка), затем в картинка-спрайт (21 колонка), затем в символ "b" (22 колонка). Спрайт больше одного знакоместа и занял больше одной колонки, но символ "b" был отрисован позже и, тем самым, поверх спрайта.

TODO: картинка-иллюстрация

Следует отметить, что спрайт всегда выводится начиная с левого верхнего угла клетки. С версии библиотеки R8, на размер спрайта особых ограничений не накладывается; разработчик должен сам следить, чтобы картинка аккуратно вписывалась в остальное текстовое содержимое на экране.

color_from_argb

<syntaxhighlight lang="cpp"> color_t color_from_argb(int a, int r, int g, int b); </syntaxhighlight> Простая вспомогательная функция, собирающая из отдельных R, G, B и A компонент одно 32-битное число, которым представляется цвет в библиотеке (0xAARRGGBB).

color_from_name

<syntaxhighlight lang="cpp"> color_t color_from_name(const char* name); </syntaxhighlight> Данная функция возвращает цвет (его числовое представление) по имени из встроенной в библиотеку палитры. Эта палитра практически полностью повторяет палитру из libtcod. Имя указывается в виде "оттенок" или "яркость оттенок", где возможными значениями являются:

Яркость lightest, lighter, light, dark, darker, darkest
Оттенок grey или gray, red, flame, orange, amber, yellow, lime, chartreuse, green, sea, turquoise, cyan, sky, azure, blue, han, violet, purple, fuchsia, magenta, pink, crimson

Также, по имени цвет можно использовать в тегах color и bkcolor функции terminal_printf: <syntaxhighlight lang="cpp"> terminal_color(color_from_name("lighter gray")); // Основным цветом выбран светло-серый terminal_printf(1, 1, "You see a [color=dark red]glowing[/color] stone."); // Слово glowing будет темно-красным </syntaxhighlight>

Кодировки

...

Ввод

...

Примеры

<syntaxhighlight lang="cpp">

  1. include <bearlibterminal.h>
  2. include <Windows.h>

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { terminal_open(CODEPAGE_DEFAULT);

// Выводим текст terminal_printf(1, 1, "Hello, world!"); terminal_refresh();

// Ждем, пока пользователь не закроет окно while ( terminal_get() != VK_CLOSE );

terminal_close(); } </syntaxhighlight> BTW, зависимость от Windows.h будет исправлена со временем.

<syntaxhighlight lang="pascal"> uses

 BeaRLibTerminal,
 Windows;

begin

 terminal_open(CODEPAGE_DEFAULT);

 // Выводим текст
 terminal_printf(1, 1, 'Hello, world!');
 terminal_refresh();
 // Ждем, пока пользователь не закроет окно
 while ( terminal_get() <> VK_CLOSE ) do ; 

 terminal_close();

end. </syntaxhighlight>