C неблокуючий введення з клавіатури


82

Я намагаюся написати програму на мові C (на Linux), яка циклічно повторюється, доки користувач не натисне клавішу, але для продовження кожного циклу не потрібно вимагати натискання клавіші.

Чи є простий спосіб це зробити? Я думаю, що я міг би це зробити, select()але це здається великою роботою.

Як варіант, чи є спосіб зловити ctrl- cнатискання клавіші для очищення до закриття програми замість того, щоб не блокувати io?


а як щодо відкриття нитки?
Nadav B

Відповіді:


65

Як уже зазначалося, ви можете використовувати sigactionдля захоплення ctrl-c або selectдля захоплення будь-якого стандартного введення.

Однак зауважте, що з останнім методом вам також потрібно встановити TTY таким чином, щоб він знаходився в режимі "символ за раз", а не "за часом". Останнє є типовим - якщо ви вводите рядок тексту, він не надсилається на stdin запущеної програми, поки ви не натиснете Enter.

Вам потрібно буде використовувати tcsetattr() функцією, щоб вимкнути режим ICANON і, можливо, також вимкнути ECHO. З пам'яті вам також доведеться перевести термінал у режим ICANON, коли програма виходить!

Просто для повноти картини , ось код , який я тільки що залетіла (нб: немає перевірки помилок) , який встановлює Unix TTY і емулює DOS <conio.h>функції kbhit()і getch():


1
Getch () повинен повертати, яку клавішу було натиснуто, або просто повинен використовувати символ?
Salepate

@Salepate має повернути персонажа. Це (void)біт отримує цього персонажа, а потім викидає його.
Alnitak

1
о, я бачу, можливо, я роблю це неправильно, але коли я використовую getch () на printf ("% c"); на екрані відображаються дивні символи.
Salepate

Невелике редагування, вам потрібно #include <unistd.h>, щоб read () працював
jernkuan

Чи є простий спосіб прочитати цілий рядок (наприклад, за допомогою 'getline') замість одного символу?
Azmeuk

15

select()є занадто низьким рівнем для зручності. Я пропоную вам скористатися ncursesбібліотекою, щоб перевести термінал у режим cbreak та режим затримки, а потім зателефонувати getch(), який повернеться, ERRякщо жоден символ не готовий:

У цей момент ви можете телефонувати getchбез блокування.


Я також думав про використання проклять, але потенційна проблема в тому, що виклик initscr () очищає екран, і це також може заважати використанню звичайного stdio для виведення на екран.
Alnitak

5
Так, прокляття - це майже все або нічого.
Норман Ремзі

11

У системах UNIX ви можете використовувати sigactionвиклик для реєстрації обробника SIGINTсигналу для сигналу, який представляє послідовність клавіш Control + C. Обробник сигналу може встановити прапорець, який перевірятиметься в циклі, змушуючи його належним чином зламатися.


Думаю, в кінцевому підсумку я зроблю це для зручності, але я прийму іншу відповідь, оскільки вона ближча до запитуваної в назві.
Zxaos

6

Ви, мабуть, хочете kbhit();

це може працювати не в усіх середовищах. Портативним способом буде створення потоку моніторингу та встановлення певного прапораgetch();


4
точно не портативний. kbhit () - це функція введення-виводу консолі DOS / Windows.
Альнітак

1
Це все ще дійсна відповідь для багатьох застосувань. В ОП не вказано переносимість або будь-яку конкретну платформу.
AShelly

4
Альнітак: Я не знав, що це означає платформу - що означає еквівалентний термін Windows?
Zxaos

3
@Alnitak, чому "неблокування", як концепція програмування, буде більш звичним у середовищі Unix?
MestreLion

4
@Alnitak: Я маю на увазі, що я був знайомий з терміном "неблокуючий дзвінок" задовго до того, як торкнутися будь-якого * nix .. так само, як і Zxaos, я ніколи не зрозумів, що це буде означати платформу.
MestreLion

4

Ще один спосіб отримати неблокуючий введення з клавіатури - це відкрити файл пристрою та прочитати його!

Ви повинні знати файл пристрою, який ви шукаєте, один із / dev / input / event *. Ви можете запустити cat / proc / bus / input / devices, щоб знайти потрібний пристрій.

Цей код працює для мене (працює як адміністратор).


фантастичний приклад, але я зіткнувся з проблемою, коли Xorg продовжував отримувати ключові події, коли пристрій події переставав видавати, коли було утримувано більше шести клавіш: \ weird right?
ThorSummoner

3

Для цього можна використовувати бібліотеку проклять. Звичайно, select()і обробники сигналів можуть також використовуватися до певної міри.


1
curses - це і назва бібліотеки, і те, що ви будете бурмотіти, коли будете користуватися нею;) Однак, оскільки я збирався згадати це вам +1.
Wayne Werner

2

Якщо ви щасливі, що просто спіймали Control-C, це вже зроблено. Якщо ви дійсно хочете неблокуючий введення-виведення, але ви не хочете бібліотеку curses, інша альтернатива - перемістити замок, запас і стовбур до бібліотеки AT&Tsfio . Це приємна бібліотека з малюнком на C, stdioале більш гнучка, безпечна для потоків і працює краще. (sfio означає безпечне, швидке введення / виведення.)


1

Існує не портативний спосіб зробити це, але select () може бути хорошим способом. Докладніше про можливі рішення див. На веб- сторінці http://c-faq.com/osdep/readavail.html .


1
ця відповідь неповна. без маніпуляцій з терміналом за допомогою tcsetattr () [див. мою відповідь] ви не отримаєте нічого на fd 0, доки не натиснете Enter.
Альнітак

0

Ви можете зробити це, використовуючи select наступним чином:

Не надто багато роботи: D


2
Ця відповідь неповна. Вам потрібно встановити термінал у неканонічний режим. Також nfdsслід встановити значення 1.
luser droog

0

Ось функція, яка робить це за вас. Вам потрібно, termios.hщо поставляється з системами POSIX.

Порушення цього: tcgetattrотримує поточну інформацію про термінал і зберігає її в t. Якщо cmdдорівнює 1, локальний прапорець введення tвстановлюється як неблокуючий вхід. В іншому випадку він скидається. Потім tcsetattrзмінює стандартне введення на t.

Якщо ви не скинете стандартне введення в кінці програми, у вас будуть проблеми в оболонці.


У операторі switch відсутня одна дужка :)
étale-cohomology
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.