BearLibTerminal
BearLibTerminal — это небольшая (в интерфейсном плане) библиотека для организации терминал-подобного окна, вывода текста, обработки простого ввода.
Большое количество roguelike совершенно осознанно используют аскетичное символьное/псевдографическое оформление. Однако, использование стандартных средств вывода (командной строки ОС) сопряжено с досадными ограничениями скорости вывода, цветовой гаммы, используемого шрифта. Нетривиальной задачей оказывается и применение расширенного набора символов, например нескольких языков и псевдографики. BearLibTerminal позволяет обойти упомянутые ограничения оставаясь в рамках простой концепции "терминала": пользователю предоставляется прямоугольная сетка ячеек-знакомест, средства для вывода текста и чтения ввода с клавиатуры.
Достойными внимания особенностями терминала являются:
- Высокая скорость вывода (в основе лежит OpenGL).
- Возможность использования тайловых (в виде картинки) и векторных (TrueType) шрифтов.
- Легкость использования Unicode.
- Полная TrueColor палитра.
- Возможность комбинации нескольких символов в одном знакоместе.
- Поддержка ввода с клавиатуры и мыши.
BearLibTerminal не является:
- Фреймворком roguelike: в библиотеке нет и никогда не будет генераторов случайных чисел, уровней или имен персонажей, механизмов расчета FOV/LOS и освещения, средств для работы с файлами или сетью.
- Графическим движком общего назначения: функциональность библиотеки сознательно ограничивается функциональностью псевдотерминала.
Похожие инструменты: Tinycurses, libtcod.
Содержание
- 1 Интерфейс библиотеки
- 1.1 terminal_open
- 1.2 terminal_close
- 1.3 terminal_options
- 1.4 terminal_put
- 1.5 terminal_color
- 1.6 terminal_bkcolor
- 1.7 terminal_blending
- 1.8 terminal_printf
- 1.9 terminal_clear
- 1.10 terminal_clear_area
- 1.11 terminal_refresh
- 1.12 terminal_has_input
- 1.13 terminal_get
- 1.14 terminal_get_str
- 1.15 terminal_state
- 1.16 terminal_use_image
- 1.17 color_from_argb
- 1.18 color_from_name
- 2 Кодировки
- 3 Ввод
- 4 Примеры
Интерфейс библиотеки
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">
- include <bearlibterminal.h>
- 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>