Как я могу фиксировать отдельные нажатия клавиш в консольном приложении unix без блокировки?

У меня очень простой TCP-сервер, написанный на C. Он работает бесконечно, ожидая соединений. В Windows я использую select для проверки активности сокета, и если его нет, у меня есть следующий код, позволяющий мне выйти, нажав q на клавиатуре:

if ( kbhit() ) {
   char c = getch();
   if ( c == 'q' ) break;
}

Это не работает в unix, поскольку kbhit не существует, а getch работает иначе. Я нашел образец кода, который использует tcsetattr для изменения настроек терминала и позволяет вводить символы посимвольно. После вызова функции init я открываю / dev / stdin (с O_NONBLOCK) и читаю символ, но read( f, &c, 1 ) блокируется до тех пор, пока не будет достигнут символ.

Я полагаю, я мог бы создать отдельный поток и заставить Это ждать неопределенно долго, а затем сигнализировать первому потоку, если пользователь нажимает «q», но это кажется немного жестким. Неужто есть способ попроще?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
1 622
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

Даже если мое приложение еще не использует select, это сработает. Тот факт, что я уже использую select, очень упрощает эту задачу и работает как шарм. Спасибо

Graeme Perrow 09.10.2008 01:45

Лучше добавьте "f" из вашего

read( f, &c, 1 )

для выбора звонка. Когда f готов к чтению, символ был нажат, и read () не будет блокироваться.

В Unix, будь то на системной консоли или в окне X-терминала, ввод-вывод с клавиатуры проходит через виртуальный терминал. В наши дни устройство / dev / tty - это обычный способ доступа к управляющему терминалу процесса. Все манипуляции с устройством, кроме открытия / закрытия / чтения / записи, обрабатываются системным вызовом ioctl (2) для этого конкретного устройства. Общее представление о том, что вы хотите сделать, таково:

Откройте управляющий терминал (который может быть, а может и не быть стандартным вводом)

Измените режим работы на этом терминале, чтобы вернуться, не дожидаясь полной строки ввода (что является нормальным значением по умолчанию)

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


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

  1. Используйте библиотеку curses (3)
  2. Старые системы стиля BSD, используйте sgttyb для установки CBREAK или RAW
  3. Posix или старые системы стиля System V используйте TCGETAW / TCSETAW, чтобы установить c_cc [VMIN] в 1 и c_cc [VTIME] в 0.

После пары ссылок в C FAQ можно найти эта страница фрагментов кода kbhit.

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