How to get keyboard input without typing enter
I'm just getting back into programming, and this is my first introduction to LINUX. I was wondering if anyone had a basic C program on how to read input from the keyboard one character at a time (without having to press enter)... Thanks!!
On Wednesday 18 February 2004 07:51, Ryan Walsh wrote:
I'm just getting back into programming, and this is my first introduction to LINUX. I was wondering if anyone had a basic C program on how to read input from the keyboard one character at a time (without having to press enter)...
This doesn't work so easily with standard C. The C library only defines getc() and fgetc() which both read a single character, but it is intentionally undefined after what user action this occurs. Since C came into existence along with Unix systems, this makes a lot of sense: Back in the old days of Unix (the early 1970s) users had terminals. They didn'd sit in front of the system console (computers where huge and noisy back then). This is why even nowadays you need to switch the user's tty (teletypes were the first terminals in the mid-60s) to a "raw" mode to prevent the user's input from being buffered until he hits the "return" key - even if that tty he is working on is really a Linux console or a xterm window. You need to fiddle with ioctl() to do that, and since there were so many different types of terminals back then a "terminal data base" (terminfo) came into existence that helps you to query a specific terminal's capabilities, to change its settings and to set the correct escape sequences to do even the most basic things (like, position the cursor). And since this is so much hassle a library was written around all that that does the dirty parts - curses, or, today's "new" and improved version, ncurses. So if you really, really still want to get single characters, read the documentation about terminal modes, terminfo, and ncurses. If it isn't really that important, you might as well accept the fact that users have to hit "return" - or simply change a message from "hit any key" to "hit return" to make your program continue. ;-) I know that C compilers on that "other" platform (whose name I will not utter here...) usually extend that plain C standard to something like cgetc() or getch() to work around that, but this is no longer standard C. Isn't it great to have that much heritage to look back upon? ;-) CU -- Stefan Hundhammer <sh@suse.de> Penguin by conviction. YaST2 Development SuSE Linux AG Nuernberg, Germany
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Tue, 17 Feb 2004 20:51:53 -1000 "Ryan Walsh" <walshfam@intergate.com> wrote:
I'm just getting back into programming, and this is my first introduction to LINUX. I was wondering if anyone had a basic C program on how to read input from the keyboard one character at a time (without having to press enter)... This is not part of standard C, but here is a program that I've had hanging around. The Unix/C FAQ also includes something similar. I've been using this for a while. Essentially, you set the terminal to raw mode, (stdin is file descriptor 0). You should use the basic I/O (eg. read(2) not the stdio routines like getc(3)). #include <stdio.h> #include <string.h> #include <unistd.h> #include <termio.h> #include <sys/ioctl.h> static struct termio save_term; void SetRaw(int fd) { struct termio s;
/* * Get terminal modes. */ (void)ioctl(fd, TCGETA, &s); /* * Save modes and set certain variables dependent on modes. */ save_term = s; /* * Set the modes to the way we want them. */ s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); s.c_oflag |= (OPOST|ONLCR|TAB3); s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); s.c_cc[VMIN] = 1; s.c_cc[VTIME] = 0; (void)ioctl(fd, TCSETAW, &s); } static void TermRestore(int fd) { /* * Restore saved modes. */ s = save_term; (void)ioctl(fd, TCSETAW, &s); } int main(int argc, char **argv) { int fd, n; char buf; fd = 0; /Set Term to raw mode */ SetRaw(fd); printf("Enter a digit, q or x to quit\n"); while(1) { n = read(fd, &buf, 1); if (n == 1) { switch(buf) { case 'q': case 'Q': case 'x': case 'X': printf("done\n"); TermRestore(fd); return 0; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': printf(" %c ", buf); fflush(stdout); } } else if (n == 0) { printf("no input\n"); TermRestore(fd); return 0; } else { TermRestore(fd); perror("read\n"); return -1; } } } - -- Jerry Feldman <gaf@blu.org> Boston Linux and Unix user group http://www.blu.org PGP key id:C5061EA9 PGP Key fingerprint:053C 73EC 3AC1 5C44 3E14 9245 FB00 3ED5 C506 1EA9 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.2 (GNU/Linux) iD8DBQFAM2xs+wA+1cUGHqkRAtc5AKCIm5+EYQaHJ0yKhm6p+PsUDPOAsACeMtU3 DM8CHhdsUH4oDxff6q1wivg= =10Rr -----END PGP SIGNATURE-----
It might be a good idea to take a quick look at the Serial-Programming-HOWTO. Depending on your distribution this is most likely in... /usr/share/doc/howto/en/html/Serial-Programming-HOWTO/index.html Chapter 2 gives a good introduction (taught me a lot anyway) to the simplest ways of waiting for input from a tty. Even though it sounds like you're not using a serial line, I think a lot of the principles will carry across and it's very concise. ----- Original Message ----- From: "Jerry Feldman" <gaf@blu.org> To: <suse-programming-e@suse.com> Sent: Wednesday, February 18, 2004 1:45 PM Subject: Re: [suse-programming-e] How to get keyboard input without typing enter
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Tue, 17 Feb 2004 20:51:53 -1000 "Ryan Walsh" <walshfam@intergate.com> wrote:
I'm just getting back into programming, and this is my first introduction to LINUX. I was wondering if anyone had a basic C program on how to read input from the keyboard one character at a time (without having to press enter)... This is not part of standard C, but here is a program that I've had hanging around. The Unix/C FAQ also includes something similar. I've been using this for a while. Essentially, you set the terminal to raw mode, (stdin is file descriptor 0). You should use the basic I/O (eg. read(2) not the stdio routines like getc(3)). #include <stdio.h> #include <string.h> #include <unistd.h> #include <termio.h> #include <sys/ioctl.h> static struct termio save_term; void SetRaw(int fd) { struct termio s;
/* * Get terminal modes. */ (void)ioctl(fd, TCGETA, &s);
/* * Save modes and set certain variables dependent on modes. */ save_term = s;
/* * Set the modes to the way we want them. */ s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); s.c_oflag |= (OPOST|ONLCR|TAB3); s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); s.c_cc[VMIN] = 1; s.c_cc[VTIME] = 0; (void)ioctl(fd, TCSETAW, &s); } static void TermRestore(int fd) { /* * Restore saved modes. */ s = save_term; (void)ioctl(fd, TCSETAW, &s); } int main(int argc, char **argv) { int fd, n; char buf; fd = 0; /Set Term to raw mode */ SetRaw(fd); printf("Enter a digit, q or x to quit\n"); while(1) { n = read(fd, &buf, 1); if (n == 1) { switch(buf) { case 'q': case 'Q': case 'x': case 'X': printf("done\n"); TermRestore(fd); return 0; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': printf(" %c ", buf); fflush(stdout); } } else if (n == 0) { printf("no input\n"); TermRestore(fd); return 0; } else { TermRestore(fd); perror("read\n"); return -1; } } }
- -- Jerry Feldman <gaf@blu.org> Boston Linux and Unix user group http://www.blu.org PGP key id:C5061EA9 PGP Key fingerprint:053C 73EC 3AC1 5C44 3E14 9245 FB00 3ED5 C506 1EA9 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.2 (GNU/Linux)
iD8DBQFAM2xs+wA+1cUGHqkRAtc5AKCIm5+EYQaHJ0yKhm6p+PsUDPOAsACeMtU3 DM8CHhdsUH4oDxff6q1wivg= =10Rr -----END PGP SIGNATURE-----
-- To unsubscribe, email: suse-programming-e-unsubscribe@suse.com For additional commands, email: suse-programming-e-help@suse.com Archives can be found at: http://lists.suse.com/archive/suse-programming-e
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wed, 18 Feb 2004 16:39:39 -0000 "Carl Peto" <carl@bookmanassociates.com> wrote:
It might be a good idea to take a quick look at the Serial-Programming-HOWTO.
Depending on your distribution this is most likely in...
/usr/share/doc/howto/en/html/Serial-Programming-HOWTO/index.html
Chapter 2 gives a good introduction (taught me a lot anyway) to the simplest ways of waiting for input from a tty.
Even though it sounds like you're not using a serial line, I think a lot of the principles will carry across and it's very concise. That is an excellent document, but it goes quite e bit beyond what the original poster wanted, and that is to get a single character from the keyboard (eg. stdin, essentially similar to the Windows conio call (I think it was getch()). But, that howto is excellent in terms of telling the reader about the various flags.
- -- Jerry Feldman <gaf@blu.org> Boston Linux and Unix user group http://www.blu.org PGP key id:C5061EA9 PGP Key fingerprint:053C 73EC 3AC1 5C44 3E14 9245 FB00 3ED5 C506 1EA9 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.2 (GNU/Linux) iD8DBQFAM8BR+wA+1cUGHqkRAtkmAJ9U8KysnYvSGcQ8AM8IjyGq/Y2nuACbBqNh J8ToyAPXodOu65yiQGxXC+M= =I29c -----END PGP SIGNATURE-----
participants (4)
-
Carl Peto
-
Jerry Feldman
-
Ryan Walsh
-
Stefan Hundhammer