tags:

views:

88

answers:

3

I need to detect a keystroke, without the user pressing enter. What's the most elegant way?

I.e. If the user hits the letter Q, without pressing enter, the program does something.

+1  A: 

Theres no good way to do this portably, as far as I know, other than to use a library like ncurses, which provides the getch(void) function.

Note: It appears getchar(void) from stdio.h waits until the enter key is pressed then feeds your the characters,s o it won't work.

mathepic
Yes, but you can turn off line buffering with raw()/cbreak() which allows the characters to return to the program instantly. See http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/init.html#RAWCBREAK for more information.
brennie
I was talking about `getchar`, not the `ncurses` function `getch`. `getch` will work fine (assuming he reads up on ncurses and raw mode), but `getchar` won't.
mathepic
On *nix, stdin is line buffered by default if connected to a tty, and fully buffered if a file or pipe. If it is a tty, then you can do things to its modes, but that generally interacts badly with stdio. ncurses knows how to do this in a somewhat platform independent way.
RBerteig
@RBerteig: line buffered vs unbuffered vs fully buffered only matter for output in stdio -- it has no effect on input.
Chris Dodd
@Chris, we're both right in end effect, but I was wrong in detail. The terminal input is almost certainly line buffered, but by the tty device driver, not stdio, and the buffering would affect read(2) as well as any library built on top of it such as stdio. You do need to politely ask the tty driver to stop buffering in a not very portable way (not even across various *nix variants, let alone Windows) to get single key strokes.
RBerteig
@RBerteig: termios should work fine on any *nix variant less than 20 years old...
Chris Dodd
A: 

In Windows <conio.h> provides funtion _getch(void) which can be used to read keystroke without echo-ing them (print them yourself if you want).

#include <conio.h>
#include <stdio.h>

int main( void )
{
   int ch;

   puts( "Type '@' to exit : " );
   do
   {
      ch = _getch();
      _putch( ch );
   } while( ch != '@' );

   _putch( '\r' );    // Carriage return
   _putch( '\n' );    // Line feed  
}
YeenFei
In Windows `<conio.h>`...
dmckee
No need to `#include <stdio.h>` here, I think.
mathepic
+3  A: 

In unix/posix, the standard way of doing this is to put the input into non-canonical mode with tcsetattr:

#include <termios.h>
#include <unistd.h>
    :
struct termios attr;
tcgetattr(0, &attr);
attr.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &attr);

See the termios(3) man page for more details (and probably more information than you wanted to know).

Chris Dodd