Ожидание ввода символа в юниксовой консоли

    В очередной раз столкнулся с проблемой блокирующего чтения символа из юниксовой консоли. Кто имел счастье писать на борландовском Паскале под DOS наверняка помнит функцию ReadKey(), блокирующую выполнение программы до нажатия эникея. У меня пару раз всплывало это требование и в юниксовой консоли, но сишная функция getchar() блокирует выполнение до нажатия Enter, что не очень желательно в некоторых случаях.

    #include <stdio.h>
    #include <termios.h>
    #include <unistd.h>
    
    int main() {
        struct termios term;
    
        tcgetattr(STDIN_FILENO, &term);
        term.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, 0, &term);
    
        if (getchar()) {
            printf(«Hello world!»);
        }
    
        return 0;
    }

    Вся соль подхода в структуре termios, которая описывает параметры терминала. Поле c_lflag отвечает за так называемые локальные опции, т.е. за то, как вводимые символы будут обработаны драйвером.

    Наш враг в данном случае — бит ICANON, отвечающий за выбор канонического или неканонического (raw) режима ввода. Канонический режим для stdin устанавливается по умолчанию и подразумевает, помимо всего прочего, строчный буфер, т.е. требование нажимать Enter каждый раз при желании скормить что-либо getchar(). Соответственно, требуется перевести stdin в неканонический режим.

    Бит ECHO того же поля termios отвечает за эхо ввода, т.е., в данном случае, отображение на экране введенного символа. Убираем/оставляем по вкусу.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 4

      +1
      Позновательно :)
        +1
        Ага, только вот программка не восстанавливает режим консоли за собой, и после этого консоль становится неюзабельной. Хотя умные люди придумали команду reset, которую можно набрать вслепую.
          0
          Для демонстрации сути не важно :)

          Добавьте в конец
          term.c_lflag &= (ICANON | ECHO);
          tcsetattr(STDIN_FILENO, 0, &term);
          0
          Неужели нет другого способа? Чтение символа разве только через сишную рантайм библиотеку можно делать?
          А системным вызовом никак нельзя?

          PS: я мало писал под линукс, но мне кажется это не единственный путь...

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое