C语言 如何处理在 C 中的 Linux 控制台中按下的键?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2984307/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:36:31  来源:igfitidea点击:

How to handle key pressed in a Linux console in C?

clinuxkeyboard

提问by mathboy

I'm using Linux console and I would like to do a program which outputs random characters until ESC is pressed. How can I make such a keyboard handler?

我正在使用 Linux 控制台,我想做一个程序,该程序在按下 ESC 之前输出随机字符。我怎样才能制作这样的键盘处理程序?

采纳答案by Andrejs Cainikovs

getch()from Curses library perhaps? Also, you will need to use notimeout() to tell getch() not to wait for next keypress.

getch()可能来自 Curses 库?此外,您需要使用 notimeout() 告诉 getch() 不要等待下一次按键。

回答by jschmier

The line discipline for a terminal device often works in canonical mode by default. In this mode, the terminal driver doesn't present the buffer to userspace until the newline is seen (Enterkey is pressed).

默认情况下,终端设备的线路规则通常在规范模式下工作。在这种模式下,终端驱动程序不会将缓冲区呈现给用户空间,直到看到换行符(Enter按下键)。

You can set the terminal into raw (non-canonical) mode by using tcsetattr()to manipulate the termiosstructure. Clearing the ECHOand ICANONflags respectively disables echoing of characters as they are typed and causes read requests to be satisfied directly from the input queue. Setting the values of VTIMEand VMINto zero in the c_ccarray causes the read request (fgetc()) to return immediately rather than block; effectively polling stdin. The call to fgetc()will return EOFif a character is not available in the stream.

您可以通过使用tcsetattr()来操作termios结构,将终端设置为原始(非规范)模式。分别清除ECHOICANON标志会禁用字符在键入时的回显,并导致直接从输入队列中满足读取请求。在数组中将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;
}

Note: This code omits error checking for simplicity.

注意:为简单起见,此代码省略了错误检查。

回答by jim mcnamara

change the tty settings for one key press:

更改一键按下的 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);
}

回答by neo730

#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);
}

In this program u will only need to press enterafter escchar,because getchar()is a blocking function. Also u may remove or decrease sleep time for child process as ur need.

在这个程序中你只需要enteresc字符后按下,因为它getchar()是一个阻塞函数。您也可以根据需要删除或减少子进程的睡眠时间。