Различать Esc и другие специальные ключи на терминале ANSI

Терминальные программы (особенно vi и vim) могут различать клавишу Esc (escape) и другие специальные клавиши, такие как клавиши со стрелками, имеющие разные команды для всех из них.

В терминальных эмуляторах, похоже, кодировку Esc используют только один шестнадцатеричный байт:

{ 1b }

Однако некоторые специальные клавиши (клавиши со стрелками, страница вверх / вниз, начало / конец, вставка / удаление) представляют собой многобайтовые последовательности, начинающиеся с того же байта:

{ 1b 5b 41 } up
{ 1b 5b 42 } down
{ 1b 5b 43 } right
{ 1b 5b 44 } left

Учитывая двойную роль этого байта, как мне определить, когда пользователь только что нажал escape, а не нажимать одну из других клавиш?

  • Я не могу дождаться следующего байта после 0x1b, так как не может быть один. Пользователь может нажать escape, а затем ждать, пока моя программа ответит на эту клавишу.

  • Я мог позвонить read() один раз, чтобы прочитать все доступные данные на терминале и проверить, является ли это только одним байтом или несколькими байтами. Это работает, при условии, что мы доверяем терминалу достаточно быстро, чтобы отправить все соответствующие байты вовремя для read() и отправляет только одно нажатие клавиши одновременно (поэтому read() не возвращает дополнительные данные или частичные нажатия клавиш). Является ли это разумным предположением в настоящее время, что люди практически всегда используют эмуляторы терминала? Как насчет сетевых ссылок (например, SSH), вызывающих задержки?

  • Я мог позвонить read() несколько раз, читая только один байт с каждым read() вызывать до тех пор, пока не останется свободных байтов. Библиотека linenoise делает это, ссылаясь на совместимость с медленными терминалами в качестве причины (но не говорит, какие именно это терминалы).

Пара общих соображений:

  • Должен ли я делать блокирующие или неблокирующие чтения? Это имеет значение?

  • Должен ли я использовать тайм-аут чтения, и если да, есть ли какое-то эмпирически определенное значение, которое часто используется?

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

#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <termios.h>

static void dump(void) {
    unsigned char bytes[8];
    ssize_t i, n;
    while ((n = read(0, bytes, sizeof(bytes))) > 0) {
        printf("{ ");
        for (i = 0; i < n; i++) {
            printf("%02x ", bytes[i]);
        }
        printf("}\r\n");
        if (bytes[0] == 'q') {
            break;
        }
    }
}

int main(void) {
    struct termios origMode;
    struct termios rawMode;
    cfmakeraw(&rawMode);
    tcgetattr(0, &origMode);
    tcsetattr(0, TCSAFLUSH, &rawMode);
    dump();
    tcsetattr(0, TCSAFLUSH, &origMode);
    return 0;
}

0 ответов

Другие вопросы по тегам