С неблокирующий ввод с клавиатуры
Я пытаюсь написать программу на C (в Linux), которая циклически работает, пока пользователь не нажмет клавишу, но не должна требовать нажатия клавиши для продолжения каждого цикла.
есть ли простой способ сделать это? Я думаю, что я мог бы сделать это с select() но это кажется много работы.
кроме того, есть ли способ поймать ctrl -c нажатие клавиши для очистки перед закрытием программы вместо неблокирующего ввода-вывода?
10 ответов:
как уже говорилось, Вы можете использовать
sigactionдля захвата ctrl-c илиselectдля захвата любого стандартного ввода.обратите внимание, однако, что с помощью последнего метода вам также нужно установить TTY так, чтобы он был в режиме символов, а не в режиме строки. Последний является стандартным - если вы вводите строку текста, она не отправляется в stdin запущенной программы, Пока вы не нажмете enter.
вам нужно использовать
tcsetattr()функция для того чтобы повернуть режим ICANON, и вероятно также отключить Эхо тоже. Из памяти, вы также должны установить терминал обратно в режим ICANON, когда программа выходит!просто для полноты, вот какой код я только что постучал (nb: нет проверки ошибок!) который устанавливает Unix TTY и эмулирует DOS
<conio.h>функцииkbhit()иgetch():#include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/select.h> #include <termios.h> struct termios orig_termios; void reset_terminal_mode() { tcsetattr(0, TCSANOW, &orig_termios); } void set_conio_terminal_mode() { struct termios new_termios; /* take two copies - one for now, one for later */ tcgetattr(0, &orig_termios); memcpy(&new_termios, &orig_termios, sizeof(new_termios)); /* register cleanup handler, and set the new terminal mode */ atexit(reset_terminal_mode); cfmakeraw(&new_termios); tcsetattr(0, TCSANOW, &new_termios); } int kbhit() { struct timeval tv = { 0L, 0L }; fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds); return select(1, &fds, NULL, NULL, &tv); } int getch() { int r; unsigned char c; if ((r = read(0, &c, sizeof(c))) < 0) { return r; } else { return c; } } int main(int argc, char *argv[]) { set_conio_terminal_mode(); while (!kbhit()) { /* do some work */ } (void)getch(); /* consume the character */ }
select()слишком низкий уровень для удобства. Я предлагаю вам использоватьncursesбиблиотека для перевода терминала в режим cbreak и режим задержки, затем вызовитеgetch(), что вернетERRесли ни один символ не готов:WINDOW *w = initscr(); cbreak(); nodelay(w, TRUE);в этот момент Вы можете позвонить
getchбез блокировки.
в системах UNIX вы можете использовать
sigactionвызов для регистрации обработчика сигнала дляSIGINTсигнал, представляющий последовательность клавиш Control+C. Обработчик сигнала может установить флаг, который будет проверен в цикле, что позволит ему сломаться соответствующим образом.
вы, вероятно, хотите
kbhit();//Example will loop until a key is pressed #include <conio.h> #include <iostream> using namespace std; int main() { while(1) { if(kbhit()) { break; } } }это может работать не во всех средах. Переносным способом было бы создать поток мониторинга и установить некоторый флаг на
getch();
еще один способ получить неблокирующий ввод с клавиатуры-открыть файл устройства и прочитать его!
вы должны знать файл устройства, который вы ищете, один из /dev/input / event*. Вы можете запустить cat /proc/bus/input/devices, чтобы найти нужное устройство.
этот код работает для меня (Запуск от имени администратора).
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <linux/input.h> int main(int argc, char** argv) { int fd, bytes; struct input_event data; const char *pDevice = "/dev/input/event2"; // Open Keyboard fd = open(pDevice, O_RDONLY | O_NONBLOCK); if(fd == -1) { printf("ERROR Opening %s\n", pDevice); return -1; } while(1) { // Read Keyboard Data bytes = read(fd, &data, sizeof(data)); if(bytes > 0) { printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code); } else { // Nothing read sleep(1); } } return 0; }
библиотека проклятий может быть использована для этой цели. Конечно,
select()и обработчики сигналов могут быть использованы в определенной степени.
Если вы счастливы просто поймать Control-C, это дело сделано. Если вы действительно хотите неблокирующий ввод-вывод, но вы не хотите библиотеку проклятий, другой альтернативой является перемещение блокировки, запаса и ствола в И
sfioбиблиотека. Это хорошая библиотека с рисунком на Cstdioно более гибкий, потокобезопасный и работает лучше. (sfio означает безопасный, быстрый ввод/вывод)
нет переносимого способа сделать это, но select() может быть хорошим способом. См.http://c-faq.com/osdep/readavail.html для более возможных решений.
вот функция, чтобы сделать это для вас. Вам нужно
termios.hкоторый поставляется с системами POSIX.#include <termios.h> void stdin_set(int cmd) { struct termios t; tcgetattr(1,&t); switch (cmd) { case 1: t.c_lflag &= ~ICANON; break; default: t.c_lflag |= ICANON; break; } tcsetattr(1,0,&t); }разорвать этот вниз:
tcgetattrполучает текущую информацию о терминале и сохраняет ее вt. Еслиcmd- это 1, флаг локального ввода вtустановлен в неблокирующий ввод. В противном случае он сбрасывается. Тогдаtcsetattrизменяет стандартный ввод наt.если вы не сбросите стандартный ввод в конце вашей программы, у вас будут проблемы в вашей оболочке.
вы можете сделать это, используя select следующим образом:
int nfds = 0; fd_set readfds; FD_ZERO(&readfds); FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */ while(1) { /* Do what you want */ int count = select(nfds, &readfds, NULL, NULL, NULL); if (count > 0) { if (FD_ISSET(0, &readfds)) { /* If a character was pressed then we get it and exit */ getchar(); break; } } }не слишком много работы: D
Comments