Как обрабатывать нажатие клавиш в консоли Linux на C? - PullRequest
14 голосов
/ 06 июня 2010

Я использую консоль Linux, и я хотел бы создать программу, которая выводит случайные символы до нажатия ESC. Как я могу сделать такой обработчик клавиатуры?

Ответы [ 4 ]

20 голосов
/ 06 июня 2010

Линейная дисциплина для оконечного устройства часто работает в каноническом режиме по умолчанию. В этом режиме драйвер терминала не представляет буфер пользовательскому пространству до появления новой строки (нажата клавиша Enter ).

Вы можете установить терминал в необработанный (неканонический) режим, используя <a href="http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html" rel="noreferrer"><b>tcsetattr()</b></a> для управления структурой termios. Очистка флагов ECHO и ICANON соответственно отключает отображение символов по мере их ввода и приводит к тому, что запросы на чтение удовлетворяются непосредственно из входной очереди. При установке значений VTIME и VMIN в ноль в массиве c_cc запрос на чтение (fgetc()) немедленно возвращается, а не блокируется; эффективно опросить стандартный ввод. Вызов 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;
}

Примечание. В этом коде пропущена проверка ошибок для простоты.

4 голосов
/ 06 июня 2010

изменить настройки 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 голосов
/ 06 июня 2010

getch () из библиотеки Curses, возможно? Также вам нужно будет использовать notimeout (), чтобы заставить getch () не ждать следующего нажатия клавиши.

0 голосов
/ 06 июня 2010
#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);
}

В этой программе вам нужно будет только нажать enter после esc char, потому что getchar() является функцией блокировки. Кроме того, вы можете удалить или уменьшить время сна для дочернего процесса по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...