0

I don't understand why the getch() function returns ERR all the time if I have an application set up in this manner (stripped to the bones):

static char data[DATA_SIZE]; // Very big - covers all input for sure

int main(int argn, char ** argv)
{

  // Slurp the file in
  int length = read(0, &data[0], DATA_SIZE);
  if (length == 0)
  {
    fprintf(stderr, "Nothing to read\n");
    return 0;
  }

  initscr();
  cbreak();
  refresh();
  WINDOW * woutput = newwin(LINES - 1, COLS, 0, 0);
  WINDOW * winput  = newwin(1, COLS, LINES - 1, 0);
  wattron(winput, A_REVERSE);
  keypad(winput, TRUE);

   //print the data buffer into a window

  int c;
  while ((c = wgetch(winput)) != 'q')
  {
  }
}

I run the application in this manner:

./application < path/to/file

But the result of wgetch is always 'ERR'.

Pietro
  • 13
  • 4
  • Ah, I see .It returns -1 - why isn't it getting my tty's input after the file ? Is there a way to get it to work that way ? – Pietro Apr 21 '20 at 16:09
  • Where is the tty's output going to ? Discarded ? Anyway, it makes sense - I don't know why I thought it would come thereafter the file end. my bad – Pietro Apr 21 '20 at 16:16
  • I think you may be right - if you set that to be an aswer I could mark it with the green flag – Pietro Apr 21 '20 at 16:37
  • Does this answer your question? [File descriptor of getch()](https://stackoverflow.com/questions/16734387/file-descriptor-of-getch) – Thomas Dickey Apr 21 '20 at 19:10

1 Answers1

0

Since you have read stdin up to the end of file, wgetch will immediately receive an end of file when it attempts to read. But in any case, you don't want ncurses to use the redirected stdin; you want it to use the terminal.

One way of doing this is to simply reopen stdin as the terminal before calling initscr:

#include <stdio.h>
#include <ncurses.h>

int main(int argn, char ** argv)
{
  // Just discard all of stdin for a minimal example
  while (fgetc(stdin) != EOF) { }

  // See note
  if (!freopen("/dev/tty", "r", stdin)) {
    perror("/dev/tty");
    exit(1);
  }
  initscr();

  cbreak();
  refresh();
  WINDOW * woutput = newwin(LINES - 1, COLS, 0, 0);
  WINDOW * winput  = newwin(1, COLS, LINES - 1, 0);
  wattron(winput, A_REVERSE);
  keypad(winput, TRUE);

  // Echo input into full screen
  int c;
  while ((c = wgetch(winput)) != 'q') { wechochar(woutput, c); }
  endwin();
  return 0;
}

If you find freopen hard to follow, you could replace it and the following call to initscr with

FILE* tty = fopen("/dev/tty", "r");
if (!tty) { perror("/dev/tty"); exit(1); }
if (!newterm(NULL, stdout, tty) {
  fprintf(stderr, "%s\n", "Unable to initialise ncurses");
  exit(1);
}

Note:

In the above code, I just used /dev/tty as the name of the console, which will probably work in any moderately Posix compliant system. However, Posix also provides an interface which lets you discover the pathname for the current "controlling terminal", and importantly lets you know if there isn't one for your process. See man ctermid for details and an example of use.

rici
  • 234,347
  • 28
  • 237
  • 341
  • I actually used dup2(ttyfd, 0) – Pietro Apr 21 '20 at 17:07
  • @pietro: Presumably after having done something like `ttyfd = fopen("/dev/tty", "r");`. That will work, too, but it doesn't reset the eof or error flags associated with `stdin`, which may cause problems. You should call `clearerr(stdin)` as well. Or just use `freopen` :-) – rici Apr 21 '20 at 17:12
  • `newterm` is the preferred call rather than making assumptions about `initscr` – Thomas Dickey Apr 21 '20 at 19:09
  • @ThomasDickey: I was going to suggest that but reopening stdin seemed simpler. Are you saying that we cannot assume that `initscr` will use `stdin`? – rici Apr 21 '20 at 19:23
  • Both are documented, but `newterm`'s easier to follow. – Thomas Dickey Apr 21 '20 at 19:51