4

I am writing a program in C that uses ncurses to check if a key is being pressed. The problem is that there is a key repeat delay.

If I for example hold the key 'a' while in the terminal there is a short delay before 'a' gets repeatedly entered. I want to be able to know if it is being pressed from the point where it is actually pressed.

How do temporarily change this delay to be equal to 0 while in the terminal? I'm currently using Mac OSX 10.10.5.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Klas. S
  • 650
  • 9
  • 21
  • Thanks for the quick answer but that does unfortunately not fix my problem. The problem cannot be solved with ncurses, it is caused by the terminal itself. – Klas. S Dec 18 '15 at 00:21
  • Setting the delay for key-repeat is not really related to detecting a key press (unless you have omitted some information from your question). See [this page](http://apple.stackexchange.com/questions/10467/how-to-increase-keyboard-key-repeat-rate-on-os-x) for OSX key-repeat rate. – Thomas Dickey Dec 18 '15 at 01:50
  • The only way to detect input as far as I'm aware is using getchar(). If I currently press and hold 'a' getchar() will read the input: 'a', '?', '?', '?', 'a', 'a', 'a', ... (Where '?' is no input at all which happens because of the key repeat delay), I want getchar() to read the input this instead: 'a', 'a', 'a', 'a', 'a', 'a', 'a', ... If there however is some way to detect key releases, please let me know as it would also be a valid solution. – Klas. S Dec 18 '15 at 09:28

2 Answers2

2

With ncurses (any curses implementation), you would use getch rather than getchar. The latter is a C standard I/O input function.

Someone suggested Create a function to check for key press in unix using ncurses, which contains an answer worth mentioning. It uses nodelay to eliminate the time normally spent in getch for successive bytes of an escape sequence. In curses, you always have a tradeoff between waiting or not, since an escape sequence may not arrive all in one read operation. The example shown there reports cases when no character is available, and pauses (sleeps) for a short time in that case.

If you only want to see the characters which are read, you could eliminate that pause (but making your program use a lot of CPU time):

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

or (recognizing that there is a tradeoff), use napms to pause a short amount of time, but lessening the CPU time used:

#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);
        }
    }
}
Community
  • 1
  • 1
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
  • I tested your code. Sorry if I was not clear enough, what I need is a way for the system to know if a key is being held down. It would also be useful to know if a key is being released. So basically I need a way to check for "KEYDOWN" and "KEYUP" events. – Klas. S Dec 18 '15 at 10:51
  • You should state these details in your question when you first ask it. I believe I addressed all of the points in the original question. – Thomas Dickey Dec 18 '15 at 23:07
2

Try this to ignore buffered key repeats:

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

In context:

//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();
}

Compile and run with gcc example.c -lncurses -oexample && ./example

JB3000
  • 21
  • 3