Вопрос:
Я использую консоль Linux, и я хотел бы сделать программу, которая выводит случайные символы до нажатия ESC. Как я могу сделать такой обработчик клавиатуры?
Лучший ответ:
getch() из библиотеки Curses возможно? Кроме того, вам нужно будет использовать notimeout(), чтобы сообщить getch(), чтобы не ждать следующего нажатия клавиши.
Ответ №1
Линейная дисциплина для терминального устройства по умолчанию работает в каноническом режиме. В этом режиме драйвер терминала не представляет буфер для пользовательского пространства до тех пор, пока не увидит новую строку (нажата клавиша Enter).
Вы можете установить терминал в необработанный (неканонический) режим, используя tcsetattr() для управления структурой termios. Очистка флагов ECHO и ICANON соответственно отключает эхо-символы символов при их вводе и вызывает запросы чтения непосредственно из входной очереди. Установка значений VTIME и VMIN в ноль в массиве c_cc заставляет запрос чтения (fgetc()) немедленно возвращаться, а не блокировать; эффективно опрос stdin. Вызов fgetc() возвращает EOF, если символ недоступен в потоке.
#define _XOPEN_SOURCE 700 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <time.h> int getkey() { int character; struct termios orig_term_attr; struct termios new_term_attr; /* set the terminal to raw mode */ tcgetattr(fileno(stdin), &orig_term_attr); memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios)); new_term_attr.c_lflag &= ~(ECHO|ICANON); new_term_attr.c_cc[VTIME] = 0; new_term_attr.c_cc[VMIN] = 0; tcsetattr(fileno(stdin), TCSANOW, &new_term_attr); /* read a character from the stdin stream without blocking */ /* returns EOF (-1) if no character is available */ character = fgetc(stdin); /* restore the original terminal attributes */ tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr); return character; } int main() { int key; /* initialize the random number generator */ srand(time(NULL)); for (;;) { key = getkey(); /* terminate loop on ESC (0x1B) or Ctrl-D (0x04) on STDIN */ if (key == 0x1B || key == 0x04) { break; } else { /* print random ASCII character between 0x20 — 0x7F */ key = (rand() % 0x7F); printf(«%c», ((key < 0x20) ? (key + 0x20) : key)); } } return 0; }
Примечание. Этот код пропускает проверку ошибок для простоты.
Ответ №2
измените настройки tty для одного нажатия клавиши:
int getch(void) { int c=0; struct termios org_opts, new_opts; int res=0; //—— store old settings ———— res=tcgetattr(STDIN_FILENO, &org_opts); assert(res==0); //—- set new terminal parms ——— memcpy(&new_opts, &org_opts, sizeof(new_opts)); new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL); tcsetattr(STDIN_FILENO, TCSANOW, &new_opts); c=getchar(); //—— restore old settings ——— res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts); assert(res==0); return(c); } Ответ №3#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> char * me = «Parent»; void sigkill(int signum) { //printf(«=== %s EXIT SIGNAL %d ===n», me, signum); exit(0); } main() { int pid = fork(); signal(SIGINT, sigkill); signal(SIGQUIT, sigkill); signal(SIGTERM, sigkill); if(pid == 0) //IF CHILD { int ch; me = «Child»; while(1) { ch = (rand() % 26) + ‘A’; // limit range to ascii A-Z printf(«%c»,ch); fflush(stdout); // flush output buffer sleep(2); // don’t overwhelm if (1 == getppid()) { printf(«=== CHILD EXIT SINCE PARENT DIED ===n»); exit(0); } } printf(«==CHILD EXIT NORMAL==n»); } else //PARENT PROCESS { int ch; if((ch = getchar())==27) kill(pid, SIGINT); //printf(«==PARENT EXIT NORMAL (ch=%d)==n», ch); } return(0); }
В этой программе u нужно только нажать enter после esc char, потому что getchar() является блокирующей функцией.
Кроме того, u может удалить или уменьшить время сна для дочернего процесса по мере необходимости.