Различать 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;
}