配置按键重复延迟以检测按键是否被按下。

4
我正在使用C语言编写一个程序,使用ncurses库检查按键是否被按下。问题在于有一个按键重复延迟。
例如,如果我在终端中按住键'a',则会有一个短暂的延迟,然后会不断输入'a'。我想要知道从实际按下键开始是否一直被按下。
如何在终端中临时将此延迟更改为0?我目前使用的是Mac OSX 10.10.5操作系统。

谢谢您的快速回答,但不幸的是,这并不能解决我的问题。这个问题不能用ncurses来解决,它是由终端本身引起的。 - Klas. S
设置按键重复延迟时间与检测按键按下并没有真正的关系(除非您在问题中省略了一些信息)。请参阅此页面以获取OSX按键重复率的详细信息:http://apple.stackexchange.com/questions/10467/how-to-increase-keyboard-key-repeat-rate-on-os-x - Thomas Dickey
据我所知,检测输入的唯一方法是使用getchar()。如果我当前按住'a',getchar()将读取输入: 'a','?','?','?','a','a','a',... (其中'?'表示没有任何输入,这是由于键重复延迟引起的),我希望getchar()读取以下输入: 'a','a','a','a','a','a','a',...如果有一种方法可以检测按键释放,请告诉我,因为这也是一个有效的解决方案。 - Klas. S
2个回答

2

尝试以下方法以忽略缓冲键重复:

int key;
if ((key = getch()) != ERR) {
  while (getch() == key);
}

在上下文中:

//example.c

#include <ncurses.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  int pos_x = 0;
  int max_x = 0, max_y = 0;
  int key = 0;
  int on = 1;

  initscr();
  noecho();
  cbreak();
  curs_set(FALSE);
  keypad(stdscr, TRUE);
  nodelay(stdscr, TRUE);
  getmaxyx(stdscr,max_y,max_x);

  while(1) {
    clear();
    mvprintw(0, 0, "Press, hold and release L-R arrow keys. Press UP/DOWN to toggle function.");
    mvprintw(1, 0, "Skip buffered repeats: %s", (on ? "ON" : "OFF"));
    mvprintw(2, pos_x, "@");
    refresh();
    usleep(50000);
    getmaxyx(stdscr,max_y,max_x);
    key = getch();

    // skip buffered repeats                                                                                     
    if (on) {
      if (key != ERR) {
        while (getch() == key);
      }
    }
    //                                                                                                           

    switch (key) {
    case KEY_LEFT:
      pos_x += (pos_x > 0 ? -1 : 0); break;
    case KEY_RIGHT:
      pos_x += (pos_x < max_x - 1 ? 1 : 0); break;
    case KEY_UP:
      on = 1; break;
    case KEY_DOWN:
      on = 0; break;
    }
  }
  endwin();
}

使用 gcc example.c -lncurses -oexample && ./example 进行编译和运行。


2
使用ncurses(任何curses实现),你应该使用getch而不是getchar。后者是一个C标准I/O输入函数。
有人建议创建一个函数,在unix中使用ncurses检查按键是否按下,其中包含值得一提的答案。它使用nodelay来消除在连续字节的转义序列中通常花费在getch上的时间。在curses中,你总是需要在等待和不等待之间做出权衡,因为转义序列可能不会在一个read操作中到达。那里展示的例子报告了没有字符可用的情况,并在这种情况下暂停(睡眠)了一段时间。
如果你只想看到读取的字符,你可以消除那个暂停(但会让你的程序使用大量的CPU时间):
#include <ncurses.h>

int kbhit(void)
{
    int ch = getch();

    if (ch != ERR) {
        ungetch(ch);
        return 1;
    } else {
        return 0;
    }
}

int main(void)
{
    initscr();

    cbreak();
    noecho();
    nodelay(stdscr, TRUE);

    scrollok(stdscr, TRUE);
    while (1) {
        if (kbhit()) {
            printw("Key pressed! It was: %d\n", getch());
        }
    }
}

或者(意识到这是一种权衡),使用napms来暂停一个的时间,但减少CPU的使用时间:
#include <ncurses.h>

int kbhit(void)
{
    int ch = getch();

    if (ch != ERR) {
        ungetch(ch);
        return 1;
    } else {
        return 0;
    }
}

int main(void)
{
    initscr();

    cbreak();
    noecho();
    nodelay(stdscr, TRUE);

    scrollok(stdscr, TRUE);
    while (1) {
        if (kbhit()) {
            printw("Key pressed! It was: %d\n", getch());
        } else {
            napms(20);
        }
    }
}

我测试了你的代码。如果我没有表达清楚,我需要的是系统知道某个键是否被按下的方法。同时,知道某个键是否被释放也很有用。因此,我需要一种检测“KEYDOWN”和“KEYUP”事件的方法。 - Klas. S
当你第一次提问时,你应该在问题中说明这些细节。我相信我已经回答了原始问题中的所有要点。 - Thomas Dickey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接