Как сделать клавиши со стрелками и backspace правильно работать при запросе ввода от пользователя в программе на C с помощью termios.h?

Таким образом, у меня есть следующий код, который в основном просто считывает вводимые пользователем символы и печатает их до ввода «q».

#include #include #include #include int main(void) { char c; static struct termios oldtio, newtio; tcgetattr(0, &oldtio); newtio = oldtio; newtio.c_lflag &= ~ICANON; newtio.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &newtio); printf("Give text: "); fflush(stdout); while (1) { read(0, &c, 1); printf("%c", c); fflush(stdout); if (c == 'q') { break; } } printf("\n"); tcsetattr(0, TCSANOW, &oldtio); return 0; } 

В начале основной функции я выключаю канонический режим, чтобы пользователь мог видеть его вход, когда он его дает. Я также отключил эхо, так что, например, «^ [[A] не появляется, когда нажимает клавишу со стрелкой вверх. Это работает, но я также могу переместить курсор в верхние строки в окне терминала, и это не хорошо. Есть ли способ исправить это, чтобы пользователь мог перемещаться только в текущей строке?

Другая проблема – это обратное пространство. Когда я нажимаю его, программа печатает странный символ (который, как я полагаю, равен 0x7f), вместо стирания символа, оставшегося до текущего местоположения курсора. Я должен как можно более эффективно обрабатывать выход ключа backspace в программе, но я не знаю, как это сделать, поскольку это странное шестнадцатеричное число. Какие-нибудь советы для этого?

Один из вариантов, о котором я также думал, заключается в том, чтобы использовать канонический режим, так что клавиши со стрелками и функциональные возможности backspace автоматически используются. Однако канонический режим работает по строкам, поэтому текст не появляется, пока пользователь не нажимает «Enter». Пока я не понял, как заставить пользователя видеть его ввод при наборе текста. Возможно ли это?

И, пожалуйста, никаких предложений ncurses или readline. Я хочу сделать это с помощью termios.h.

    вы заглянули в страницы руководства? (должен быть man termios или смотреть где-то в Интернете )

    Там я нашел флаг ECHOE который, как говорят, имеет следующий эффект:

    Если ICANON также установлен, символ ERASE стирает предыдущий входной символ, а WERASE стирает предыдущее слово.

    Это должно устранить проблему с обратным пространством?

    Я также предлагаю вам ознакомиться с примерами на странице руководства. Например, вы можете сделать следующее:

     newtio.c_lflag &= ~(ECHO | ECHOE | ICANON); 

    … для установки более одного флага за раз только в одной строке. Я знаю, что страницы руководства трудно читать для новичков, но вы привыкнете к ним, и чем больше вы их используете, тем эффективнее они будут искать функции C / POSIX и т. Д. (На всякий случай, вы не используете их тем не мение).

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

    BTW: termios выглядит интересно, я всегда задавался вопросом, какие функции используют некоторые программы командной строки; узнал что-то по вашему вопросу, спасибо!

    РЕДАКТИРОВАТЬ

    В эти выходные я провел еще несколько исследований. Символ «странный», напечатанный при нажатии клавиши «назад», довольно легко скрыть. Это значение ASCII 0x7f . Поэтому добавьте простой

     if (c == 0x7f) { continue; } 

    … просто игнорировать клавишу backspace. Или обработайте его таким образом, чтобы удалить последний символ (см. Пример кода ниже).

    Это простое решение не работает для клавиш со стрелками, хотя они не являются символами ASCII: / Однако эти две темы помогли мне справиться и с этой проблемой: тема 1 и тема 2 . В основном нажатие клавиш со стрелками приводит к последовательности нескольких символов, отправляемых на stdin (см. Вторую ссылку для получения дополнительной информации).

    Вот мой полный код, который (я думаю) работает так, как вы пожелаете:

     #include  #include  #include  #include  int main(void) { char c; static struct termios oldtio, newtio; tcgetattr(0, &oldtio); newtio = oldtio; newtio.c_lflag &= ~ICANON; newtio.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &newtio); printf("Give text:\n"); fflush(stdout); while (1) { c = getchar(); // is this an escape sequence? if (c == 27) { // "throw away" next two characters which specify escape sequence c = getchar(); c = getchar(); continue; } // if backspace if (c == 0x7f) { // go one char left printf("\b"); // overwrite the char with whitespace printf(" "); // go back to "now removed char position" printf("\b"); continue; } if (c == 'q') { break; } printf("%c", c); } printf("\n"); tcsetattr(0, TCSANOW, &oldtio); return 0; } 

    BTW вы можете получить полные escape-последовательности по следующему коду:

     int main(void) { char c; while (1) { c = getchar(); printf("%d", c); } return 0; } 

    Я думаю, что мне не нужно говорить, что эта полная вещь – довольно грязный хак, и легко забыть обращаться с некоторыми специальными ключами. Например, в моем коде я не обрабатываю кнопки page-up/down или home … -> приведенный выше код далеко не завершен, но дает вам возможность начать. Вы также должны взглянуть на terminfo который может предоставить вам много необходимой информации; это также должно помочь с более переносимым решением. Как видите, эта «простая» вещь может стать довольно сложной. Поэтому вы можете пересмотреть свое решение против ncurses 🙂

    Фактически, для обработки клавиш со стрелками вам нужно будет реализовать кусок ncurses большого размера. Есть плюсы и минусы: основным недостатком использования ncurses в приложении из командной строки может быть то, что он обычно очищает экран. Однако (n) curses предоставляет функциональный фильтр . В источнике ncurses есть пример программы «test / filter.c», который иллюстрирует это с помощью клавиши со стрелкой влево как символ стирания и передает полученную строку в system () для запуска простых команд. Кажется, что образец меньше 100 строк кода – проще и полнее, чем приведенные выше примеры.