【问题标题】:Problem with kbhit()[and getch()] for LinuxLinux 的 kbhit()[和 getch()] 的问题
【发布时间】:2009-10-03 13:25:15
【问题描述】:
while(ch != 'q') 
{
  printf("looping\n");
  sleep(1);
  if(kbhit()) 
   {
    ch = readch();
    printf("you hit %c\n",ch);
   }
}

此代码为我提供了一个类似 getch() 的阻塞功能。我正在尝试使用此代码来捕获向上向下箭头键。

添加: 尝试捕获向上箭头的关键代码给了我 3 个字符 27、91 和 65。 使用 if/else 我正在尝试模式匹配,但我只得到 2 个字符。按下下一个键时捕获下一个。

我想使用 getchar() 捕获完整的单词,同时始终寻找某些键(esc、del 等)。

【问题讨论】:

  • 箭头键(甚至更多,功能键)通常会在终端模拟器中生成多个字节。您可以查看 /usr/share/lib/terminfo 中的终端定义(使用 infocmp 获取文本表示)以查看过去发送的序列的多样性。如今,真正的绿屏越来越少(大多数终端都是运行终端模拟器的窗口),因此需要处理的古怪终端类型也越来越少。
  • 密切相关(但不相同)的问题:stackoverflow.com/questions/267250;这也是相关的:stackoverflow.com/questions/905060
  • 字符代码 27(又名 033 或 0x1B)是 ESC 或转义。字符代码 91 是 '[';字符代码 65 是 A。您的终端生成向上箭头的 3 个字符序列 - 所以它或多或少模拟了 ANSI 终端。

标签: c console inputstream getch


【解决方案1】:

我无法重现您的问题:

#include <unistd.h> 
#include <stdio.h> 
#include <ctype.h> 

#include "kbhit.h" /* http://linux-sxs.org/programming/kbhit.html */

int main(){
  init_keyboard();
  char ch='x';
  while( ch != 'q' ){
    printf("looping\n");
    sleep(1);
    if( kbhit() ){
      printf("you hit");
      do{
        ch = readch();
        printf(" '%c'(%i)", isprint(ch)?ch:'?', (int)ch );
      }while( kbhit() );
      puts("");
    }
  }
  close_keyboard();
}

【讨论】:

【解决方案2】:

这个例子可能会有所帮助:

raw.c - 原始模式演示

/* Raw mode demo */
/* See exactly what is being transmitted from the terminal. To do this
   we have to be more careful. */

#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>

struct termios oldtermios;

int ttyraw(int fd)
{
    /* Set terminal mode as follows:
       Noncanonical mode - turn off ICANON.
       Turn off signal-generation (ISIG)
        including BREAK character (BRKINT).
       Turn off any possible preprocessing of input (IEXTEN).
       Turn ECHO mode off.
       Disable CR-to-NL mapping on input.
       Disable input parity detection (INPCK).
       Disable stripping of eighth bit on input (ISTRIP).
       Disable flow control (IXON).
       Use eight bit characters (CS8).
       Disable parity checking (PARENB).
       Disable any implementation-dependent output processing (OPOST).
       One byte at a time input (MIN=1, TIME=0).
    */
    struct termios newtermios;
    if(tcgetattr(fd, &oldtermios) < 0)
        return(-1);
    newtermios = oldtermios;

    newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    /* OK, why IEXTEN? If IEXTEN is on, the DISCARD character
       is recognized and is not passed to the process. This 
       character causes output to be suspended until another
       DISCARD is received. The DSUSP character for job control,
       the LNEXT character that removes any special meaning of
       the following character, the REPRINT character, and some
       others are also in this category.
    */

    newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    /* If an input character arrives with the wrong parity, then INPCK
       is checked. If this flag is set, then IGNPAR is checked
       to see if input bytes with parity errors should be ignored.
       If it shouldn't be ignored, then PARMRK determines what
       character sequence the process will actually see.

       When we turn off IXON, the start and stop characters can be read.
    */

    newtermios.c_cflag &= ~(CSIZE | PARENB);
    /* CSIZE is a mask that determines the number of bits per byte.
       PARENB enables parity checking on input and parity generation
       on output.
    */

    newtermios.c_cflag |= CS8;
    /* Set 8 bits per character. */

    newtermios.c_oflag &= ~(OPOST);
    /* This includes things like expanding tabs to spaces. */

    newtermios.c_cc[VMIN] = 1;
    newtermios.c_cc[VTIME] = 0;

    /* You tell me why TCSAFLUSH. */
    if(tcsetattr(fd, TCSAFLUSH, &newtermios) < 0)
        return(-1);
    return(0);
}

int ttyreset(int fd)
{
    if(tcsetattr(fd, TCSAFLUSH, &oldtermios) < 0)
        return(-1);

    return(0);
}

void sigcatch(int sig)
{
    ttyreset(0);
    exit(0);
}

int main()
{
    int i;
    char c;

    /* Catch the most popular signals. */
    if((int) signal(SIGINT,sigcatch) < 0)
    {
        perror("signal");
        exit(1);
    }
    if((int)signal(SIGQUIT,sigcatch) < 0)
    {
        perror("signal");
        exit(1);
    }
    if((int) signal(SIGTERM,sigcatch) < 0)
    {
        perror("signal");
        exit(1);
    }

    /* Set raw mode on stdin. */
    if(ttyraw(0) < 0)
    {
        fprintf(stderr,"Can't go to raw mode.\n");
        exit(1);
    }

    while( (i = read(0, &c, 1)) == 1)
    {
        if( (c &= 255) == 0177) /* ASCII DELETE */
            break;
        printf( "%o\n\r", c);
    }

    if(ttyreset(0) < 0)
    {
        fprintf(stderr, "Cannot reset terminal!\n");
        exit(-1);
    }

    if( i < 0)
    {
        fprintf(stderr,"Read error.\n");
        exit(-1);
    }

    return 0;
}

(停止演示的退格键,origin

【讨论】:

  • 我看过这段代码,我的代码给了我类似的代码。但是我如何使用 UP 键的三个代码进行模式匹配。我等待按下下一个键。
  • 修改例子为:char pp = 0;字符 p = 0; while( (i = read(0, &c, 1)) == 1) { if (pp == 033 && p == 0133 && (c &= 255) == 0102) /* DOWN / break; if (c == 0177) / ASCII DELETE */ break; printf("%o, %o, %o\t%s\n\r", pp, p, c, &c); p = p; p = c; } 在我的 linux 机器上工作,但如果你可以使用它,我会建议 curse(见 linuxselfhelp.com/HOWTO/NCURSES-Programming-HOWTO/keys.html
  • 这使用了read(),之后我不能使用getchar()。我正在使用 getchar() 来捕获完整的单词。还一直在寻找某些键(esc、del 等)
猜你喜欢
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多