BearLibTerminal / API / Ввод

Функции

В BearLibTerminal ввод обрабатывается с помощью функций read, peek, read str, has input, state и check.

read

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

int key = terminal_read();

if (key == TK_ESCAPE)
{
    // Quit
}

Если в очередь ввода пуста, вызов заблокирует выполнение до поступления нового события. Если такое блокируещее поведение нежелательно, можно проверить has input перед вызовом read.

peek

Эта функция аналогична read, но не убирает прочитанное событие из внутренней очереди (следующий вызов peek или read вернет то же значение). Кроме того, вызов этой функции является неблокирующим: если очередь ввода пуста, функция вернет 0.

read_str

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

Функция возвращает размер строки, введенной пользователем и подтвержденной нажатием на Enter, или константу TK_INPUT_CANCELLED, если ввод был отменен нажатием на Escape или вовсе попыткой закрытия окна.

wchar_t s[32] = L"Hello";

if (terminal_read_wstr(2, 1, s, sizeof(s)-1) >= 0)
{
    terminal_wprintf(L"You entered: \"%ls\"", s);
}

Если поведение read_str не удовлетворяет требованиям конкретного приложения, можно использовать состояния ввода TK CHAR/TK WCHAR, позволяющие реализовать произвольный ввод строки.

has_input

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

while (terminal_has_input())
{
    int key = terminal_read();
}

state

Эта функция возвращает текущее численное значение некоторого свойства/состояния библиотеки. При обработке ввода с помощью state запрашиваются различные своства событий ввода, см. таблицу ниже.

int key = terminal_read();

if (key == TK_MOUSE_MOVE)
{
    int x = terminal_state(TK_MOUSE_X);
    int y = terminal_state(TK_MOUSE_Y);
}

Следует отметить, что state, в общем виде, возвращет значение свойства не на момент вызова, а на момент, когда случилось последнее выбранное из очереди событие ввода, см. очередь ввода.

check

Эта функция является простой оберткой над state. Во многих языках программирования нельзя сравнивать числовые и булевы значения, из-за чего проверка логически булевых состояний посредством постоянного приведения численного результата state к булевому типу может быстро надоесть. Вместо этого можно использовать check для проверки свойств:

int key = terminal_read();

if (key == TK_Z)
{
    if (terminal_check(TK_SHIFT))
    {
        // 'Z': использовать жезл
    }
    else
    {
        // 'z': прочитать заклинание
    }
}

Опции ввода

Опции ввода устанавливаются с помощью функции set:

terminal_set
(
    "input:"
    "cursor-symbol = 0x1F,"
    "cursor-blink-rate = 500,"
    "precise-mouse = false,"
    "mouse-cursor = true,"
    "filter=[keyboard];"
);

input.cursor-symbol

Символ/тайл, используемый в качестве курсора функцией read str. По умолчанию это “_” (символ подчеркивания) и может быть сменен, чтобы соответствовать интерфейсу приложения, например на сплошной прямоугольник или вертикальную черту.

Скорость, с которой мерцает курсор в функции read str, в миллисекундах. По умолчанию 500, т. е. дважды в секунду.

input.precise-mouse

Булев флаг, контролирующий генерацию событий о движении курсора мыши. Если он false, событие генерируется только если курсор перемещается с одного знакоместа на другое. Если флаг true, любое перемещение курсора мыши генерирует событие. По умолчанию параметр выставлен в false.

input.mouse-cursor

Булев флаг, контролирующий видимость курсора мыши. По умолчанию true.

input.sticky-close

Булев флаг, контролирующий реакцию терминала на событие TK_CLOSE. Если он true, единожды сгенерированное событие TK_CLOSE не может быть сброшено, а все последующие вызовы read будут возвращать TK_CLOSE. Обычно это несколько упрощает логику приложения. Когда параметр выставлен в false, событие TK_CLOSE имеет смысл запроса и приложение может его проигнорировать, например спросив подтверждение у пользователя. По умолчанию параметр выставлен в true.

input.filter

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

input.filter = [keyboard, mouse+]

Именем события может быть:

  • группа событий: keyboard (вся клавиатура), mouse (мышь), keypad (цифровой блок-нумпад), arrows (стрелки).
  • имя одного события как указано в таблице ниже, но без префикса 'TK_'; регистр не имеет значения.
  • краткое перечисление буквенно-цифровых событий, например “abc” или “12345”.

Указание имени события или группы событий “включает” их чтение посредством функции read. Если к имени добавлен знак ”+”, то будут докладываться как нажатия клавиши, так и отпускания.

Фильтр по умолчанию – '​keyboard'​. Для того, чтобы задействовать мышь, нужно установить фильтр в '​keyboard,​ mouse'​. “Системные” события TK_CLOSE и TK_RESIZED задействованы всегда.

Если список пуст, фильтрация не производится. Для того, чтобы выключить фильтрацию, достаточно установить пустой список в качестве значения input.filter.

Константы свойств и событий

Группа Константа Описание
Буквенно-цифровые TK_A … TK_Z Событие: сигнализирует о том, что соответствующая клавиша была нажата или отпущена. Если клавиша была отпущена, то ее коду будет добавлена констатна TK_KEY_RELEASED (еще...).

Свойство: текущее состояние клавиши (см. очередь ввода).
TK_0 … TK_9
TK_SPACE
TK_MINUS -
TK_EQUALS =
TK_LBRACKET [
TK_RBRACKET ]
TK_BACKSLASH \
TK_SEMICOLON ;
TK_APOSTROPHE '
TK_GRAVE `
TK_COMMA ,
TK_PERIOD .
TK_SLASH /
Функциональные,
навигационные,
регистровые
TK_F1 … TK_F12
TK_RETURN
TK_ESCAPE
TK_BACKSPACE
TK_TAB
TK_PAUSE
TK_INSERT
TK_HOME
TK_PAGEUP
TK_DELETE
TK_END
TK_PAGEDOWN
TK_RIGHT
TK_LEFT
TK_DOWN
TK_UP
TK_SHIFT
TK_CONTROL
Нумпад TK_KP_0 … TK_KP_9
TK_KP_DIVIDE /
TK_KP_MULTIPLY *
TK_KP_MINUS -
TK_KP_PLUS +
TK_KP_PERIOD .
TK_KP_ENTER
Мышь TK_MOUSE_LEFT
TK_MOUSE_RIGHT
TK_MOUSE_MIDDLE
TK_MOUSE_X1
TK_MOUSE_X2
TK_MOUSE_MOVE Событие: генерируется, когда курсор мыши перемещается с одного знакоместа на другое (иными словами, с точностью “до знакоместа”). Если включена опция input.precise-mouse, событие генерируется при любом движении курсора мыши.
TK_MOUSE_SCROLL Событие: генерируется при повороте колесика мыши.
TK_MOUSE_WHEEL Свойство: возвращает дельту поворота колесика мыши последнего события TK_MOUSE_SCROLL (еще...).
TK_MOUSE_X Свойство: положение курсора мыши, в знакоместах.
TK_MOUSE_Y
TK_MOUSE_PIXEL_X Свойство: положение курсора мыши, в пикселях. Эти свойства доступны вне зависимости от опции input.precise-mouse.
TK_MOUSE_PIXEL_Y
TK_MOUSE_CLICKS Свойство: количество быстрых последовательных щелчков на момент последнего события нажатия кнопки мыши, например 1 для обычного щелчка, 2 для двойного и т. д. (еще...)
Другие свойства TK_WIDTH Свойство: размер терминала в знакоместах.
TK_HEIGHT
TK_CELL_WIDTH Свойство: размер знакоместа в пикселях.
TK_CELL_HEIGHT
TK_COLOR Свойство: текущий цвет переднего плана.
TK_BKCOLOR Свойство: текущий цвет фона.
TK_LAYER Свойство: текущий номер слоя.
TK_COMPOSITION Свойство: текущий флаг композиции.
TK_CHAR Свойство: Unicode или ANSI код символа, ассоциированного с последним прочитанным событием ввода (еще...).
TK_WCHAR
TK_EVENT Свойство: код последнего прочитанного события ввода.
TK_FULLSCREEN Свойство: флаг активности полноэкранного режима.
Другие события TK_CLOSE Событие: запрос на закрытие терминала, генерируемый когда пользователь пытается закрыть окно, например щелкнув по кнопке закрытия или нажав Alt+F4 (см. опцию input.sticky-close).
TK_RESIZED Событие: размер окна был изменен пользователем (см. опцию window.resizeable).

Очередь ввода

Все генерируемые события ввода попадают во внутреннюю очередь. Чтение этой очереди производится с помощью функции read, причем до тех пор, пока событие не будет выбрано из очереди, оно не окажет никакого воздействия на приведенные выше свойства. У такого механизма есть весьма важное последствие: все связанные со вводом свойства всегда находятся в согласованном состоянии.

Например, пусть пользователь нажал A, а затем Shift+B. Будут сгенерированы следующие события: A⇩ A⇧ Shift⇩ B⇩ Shift⇧ B⇧. Когда приложение в процессе чтения очереди ввода наткнется на некоторое событие нажатия клавиши, можно будет проверить была ли нажата одна клавиша или целая комбинация простым вызовом функции check:

if (key == TK_A && terminal_check(TK_SHIFT))
{
    // Do something
}

Для события A⇩ это условие будет ложным. Для B⇩ ситуация отличается тем, что к тому моменту событие Shift⇩ уже будет выбрано и применено и check вернет true.

Во многих библиотеках проблема проверки нажатия комбинации клавиш решается введением целой структуры события с кодом нажатой клавиши и набором флагов состояния клавиш Shift, Ctrl, клавиш мыши и т. д. BearLibTerminal подходит к этой проблеме несколько по-иному: все связанные со вводом свойства ведут себя как одна большая структура, описывающая произошедшее событие ввода:

if (key == TK_F && terminal_check(TK_SHIFT))
{
    // Shift+F: поиск, а не выстрел.
}
else if (key == TK_MOUSE_LEFT && terminal_check(TK_CONTROL))
{
    // Ctrl+LMB: команда, а не выбор
}
else if (terminal_state(TK_CHAR) == '?' && terminal_state(TK_MOUSE_Y) < 20)
{
    // Нажатие '?' в то время как курсор мыши находится выше 20-й строки: что бы это ни было
}

Следует отметить, что ряд свойств терминала не зависят от очереди и всегда возвращают актуальное состояние, потому что иначе они имели бы мало смысла. Этими свойствами являются: TK_WIDTH, TK_HEIGHT, TK_CELL_WIDTH, TK_CELL_HEIGHT, TK_COLOR, TK_BKCOLOR, TK_LAYER, TK_COMPOSITION, TK_FULLSCREEN.

Флаг TK_KEY_RELEASED

Эта константа используется для различения нажатия и отпуска клавиши. Для события нажатия клавиши или автоповтора, функци read вернет просто соответствующий код клавиши, например TK_A или TK_BACKSPACE. Для отпущенной же клавиши будет сгенерировано событие с кодом, составленным из кода клавиши и констатнты TK_KEY_RELEASED. Это позволяет легко фильровать события ввода, так как события нажатия и отпуска клавиши имеют различные коды, но при этим могут быть легко сопоставлены:

int key = terminal_read();

if (key == TK_A)
{
    // Нажато 'A'
    foo();
}
else if (key == TK_B|TK_KEY_RELEASED)
{
    // Отпущена 'B'
    bar();
}
else if ((key & ~TK_KEY_RELEASED) == TK_C)
{
    // Клавиша 'C' либо нажата, либо отпущена
    baz();
}

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

Свойства TK_CHAR/TK_WCHAR

Эти свойства предоставляют способ организации собственного ввода текста. Они возвращают код символа, ассоциированного с последним прочитанным событием ввода. TK_WCHAR возвращает код символа в Unicode, тогда как TK_CHAR – код ANSI, транслированный в соответствии с опцией terminal.encoding.

std::wstring my_read_str(int x, int y, int max)
{
    std::wstring s;

    while (true)
    {
        int key = terminal_read();
    
        if (key == TK_RETURN || key == TK_ESCAPE || key == TK_CLOSE)
        {
            // Не делаем различия между подтвеждением и отменой ввода
            break;
        }
        else if (key == TK_BACKSPACE && s.length() > 0)
        {
            // Удаляем один символ с конца
            s.resize(s.length()-1);
        }
        else if (terminal_check(TK_WCHAR) && s.length() < max)
        {
            // Добавляем символ в конец
            s += (wchar_t)terminal_state(TK_WCHAR);
        }
        
        // Визуализация ввода
        terminal_wprintf(x, y, L"%-*ls_", max, s.c_str());
    }
    
    return s;
}

Свойство TK_MOUSE_WHEEL

Это свойство возвращает направление и дельту поворота колесика мыши последнего события TK_MOUSE_SCROLL, например 1 или -2:

int key = terminal_read();

if (key == TK_MOUSE_SCROLL)
{
    int amount = terminal_state(TK_MOUSE_WHEEL);
    
    if (mouseOverFoo)
    {
        fooValue += amount;
    }
    else if (mouseOverBar)
    {
        barValue += amount;
    }
}

Свойство TK_MOUSE_CLICKS

Это свойство возвращает количество быстрых последовательных щелчков кнопкой мыши для последнего события нажатия клавиши мыши:

int key = terminal_read();

if (key == TK_MOUSE_LEFT)
{
    int clicks = terminal_state(TK_MOUSE_CLICKS);
    
    if (clicks == 1)
    {
        // Выбрать объект под курсором мыши
    }
    else if (clicks == 2)
    {
        // Выполнить какое-либо действие с выделенным объектом.
    }
}