Search code examples
cncurses

Why does getch() read EOF after calling halfdelay() with a very short delay?


I'm experminenting with ncurses and I've stumbled upon a bit counter-intuitive behaviour when usinghalfdelay with small delays.

The following expects keyboard input from the user. Rather than just pressing and releasing a key, I'm testing it with the key being pressed so that it should continue printing the corresponding character (more precisely, the corresponding int value). Here's a stripped version of what I have:

#include <ncurses.h>

int main() {
  int c = 0, d = 0, e = 0;

  initscr();
  cbreak();
  noecho();
  keypad(stdscr, TRUE);

  while ('q' != (c = getch())) {
    printw("c: %d\n", c);

    // Make sure that halfdelay returned OK. It should as the input is in the expected range ([1, 255])
    if ((d = halfdelay(1)) != OK) {
      printw("d: %d\n", d);
      return 0;
    }

    e = getch();

    printw("e: %d\n", e);
    cbreak();
  }

  endwin();
  return 0;
}

Here's how I build it and run it:

$ gcc -Wall -Wpedantic -lncurses file.c
$ a.out

And the output when running it (after pressing and holding a on the keyboard for ~1s):

c: 97
e: -1
c: 97
e: 97
c: 97
e: 97
c: 97
e: 97
c: 97
e: 97
c: 97
e: 97
c: 97
e: -1

Question Where is -1/EOF coming from in this output? I did notice that by increasing the delay (from 1 to 7 on my machine) makes the EOF character to go away completely. I would love to understand what's the mechanism causig this.


Solution

  • tl;dr If you type too slow, you don't get a key with getch but you get EOF instead.

    Long version:

    According to ncurses manual, halfdelay mode means that input functions wait until a key is pressed or a given timeout interval expires. With halfdelay(1) you not only set the input mode to halfdelay but you also set this interval to 1/10 second. This means if you don't get a key within 1/10s, the function returns anyway.

    On the other side holding down a key creates multiple key presses. If the speed of these keypresses is too slow to fit into your halfdelay interval, you run out of key presses at some time and will see EOF.

    Therefore it is straight forward that you can avoid getting EOF if you increase the interval beyond the interval of key presses.

    To verify the numbers you could try to figure out what the key repeat rate for holding down a key is. It's probably less than 7 per second.