0

I'm trying to use "select()" to test if a key has been struck and then read it. It sort of works but only if {Enter} is pressed after the character.
Sample code is as follows:

//  selectkb.c

#include <stdio.h>
#include <sys/select.h>

//@ Main program
int
main(
  int argc,
  char **argv)
{
    int n;
    fd_set readfds;

    FD_ZERO( &readfds );
    FD_SET( 0, &readfds );
    printf( "calling select on fd 0...\n" );
    n = select( 1, &readfds, NULL, NULL, NULL );
    printf( "select reports %d fd ready\n", n );

    if( FD_ISSET( 0, &readfds ) ) {
      char c;
      printf( "select reports fd 0 ready.\n" );
      c = getchar();
      printf( "getchar returned \"%c\"\n", c );
    }
    else {
      printf( "fd 0 not ready.\n" );
    }

    return( 0 );
}

If I press A nothing happens, but if I Press A{Enter}, the output is:

calling select on fd 0...
select reports 1 fd ready
select reports fd 0 ready.
getchar returned "A"

The output is the same if I press ABC{Enter}

Why is the {Enter} required?

(Note: I know there are other ways to do this, but in my actual app, I select on some sockets as well as fd0, but I omitted that for succinctness)

DontPanic
  • 2,164
  • 5
  • 29
  • 56
  • 1
    Does this answer your question? [Confusion about raw vs. cooked terminal modes?](https://stackoverflow.com/questions/13104460/confusion-about-raw-vs-cooked-terminal-modes) – Ben Voigt Jan 14 '22 at 21:58
  • Specifically, in cooked mode the terminal (or pseudo-tty) doesn't actually post the input to your `stdin` until the user presses Enter. – Ben Voigt Jan 14 '22 at 21:59
  • @Ben, I think you hit the nail on the head. Now I have to figure out how to fix it... – DontPanic Jan 15 '22 at 18:02

1 Answers1

1

I found the solution based on a response from @Ben Voigt. Apparently, by default, the terminal operates in "Canonical" (cooked) mode wherein the kernel does not deliver characters until an {Enter} is pressed. The solution is to set the terminal to non-canonical (raw) mode. The easiest way to do this is using termios as shown in the updated code below. When this is done, characters are delivered one-by-one and select() behaves as I want.

//  selectkb.c

#include <stdio.h>
#include <sys/select.h>
#include <termios.h>


//@ Main program
int
main(
  int argc,
  char **argv)
{
    int n;
    fd_set readfds;
    struct termios attr;

    // SET RAW MODE
    tcgetattr( 0, &attr );
    attr.cflag &= ~ICANON;
    tcsetattr( 0, TCSANOW, &attr );

    FD_ZERO( &readfds );
    FD_SET( 0, &readfds );
    printf( "calling select on fd 0...\n" );
    n = select( 1, &readfds, NULL, NULL, NULL );
    printf( "select reports %d fd ready\n", n );

    if( FD_ISSET( 0, &readfds ) ) {
      char c;
      printf( "select reports fd 0 ready.\n" );
      c = getchar();
      printf( "getchar returned \"%c\"\n", c );
    }
    else {
      printf( "fd 0 not ready.\n" );
    }

    return( 0 );
}
DontPanic
  • 2,164
  • 5
  • 29
  • 56