I'm using Linux console and I would like to do a program which outputs random characters until ESC is pressed. How can I make such a keyboard handler?
-
similar and/or related - [C non-blocking keyboard input](http://stackoverflow.com/q/448944/203667) – jschmier Jan 10 '11 at 16:00
4 Answers
The line discipline for a terminal device often works in canonical mode by default. In this mode, the terminal driver doesn't present the buffer to userspace until the newline is seen (Enter key is pressed).
You can set the terminal into raw (non-canonical) mode by using tcsetattr()
to manipulate the termios
structure. Clearing the ECHO
and ICANON
flags respectively disables echoing of characters as they are typed and causes read requests to be satisfied directly from the input queue. Setting the values of VTIME
and VMIN
to zero in the c_cc
array causes the read request (fgetc()
) to return immediately rather than block; effectively polling stdin. The call to fgetc()
will return EOF
if a character is not available in the stream.
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
int getkey() {
int character;
struct termios orig_term_attr;
struct termios new_term_attr;
/* set the terminal to raw mode */
tcgetattr(fileno(stdin), &orig_term_attr);
memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
new_term_attr.c_lflag &= ~(ECHO|ICANON);
new_term_attr.c_cc[VTIME] = 0;
new_term_attr.c_cc[VMIN] = 0;
tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
/* read a character from the stdin stream without blocking */
/* returns EOF (-1) if no character is available */
character = fgetc(stdin);
/* restore the original terminal attributes */
tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
return character;
}
int main()
{
int key;
/* initialize the random number generator */
srand(time(NULL));
for (;;) {
key = getkey();
/* terminate loop on ESC (0x1B) or Ctrl-D (0x04) on STDIN */
if (key == 0x1B || key == 0x04) {
break;
}
else {
/* print random ASCII character between 0x20 - 0x7F */
key = (rand() % 0x7F);
printf("%c", ((key < 0x20) ? (key + 0x20) : key));
}
}
return 0;
}
Note: This code omits error checking for simplicity.

- 347,512
- 102
- 1,199
- 985

- 15,458
- 6
- 54
- 72
-
1This seems to work but it also seems to provide the entire buffer each time. So if I press a then b then c, after the c press it displays aababc – Jackie Jun 29 '13 at 14:03
-
@Jackie maybe do a `while (getchar() != EOF);` after `character = fgetc(stdin);` – Ciro Santilli OurBigBook.com Jul 02 '16 at 16:45
-
change the tty settings for one key press:
int getch(void) {
int c=0;
struct termios org_opts, new_opts;
int res=0;
//----- store old settings -----------
res=tcgetattr(STDIN_FILENO, &org_opts);
assert(res==0);
//---- set new terminal parms --------
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
c=getchar();
//------ restore old settings ---------
res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
assert(res==0);
return(c);
}

- 16,005
- 2
- 34
- 51
-
Doesn't `ICRNL` go into the `c_iflag` field, not the `c_lflag` field? – Dan Moulding Sep 07 '12 at 14:17
getch() from Curses library perhaps? Also, you will need to use notimeout() to tell getch() not to wait for next keypress.

- 27,428
- 2
- 75
- 95
-
1You should explicitly mention you're talking about the (N)curses library. – James Morris Jun 06 '10 at 13:56
-
4note: getch() from ncurses needs proper ncurses "screen" to be initialized, or it won't work. – ShinTakezou Jun 06 '10 at 17:56
-
2You also have to initialize the library - which is a variation on what @ShinTakezou said. – Jonathan Leffler Jun 06 '10 at 22:04
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
char * me = "Parent";
void sigkill(int signum)
{
//printf("=== %s EXIT SIGNAL %d ===\n", me, signum);
exit(0);
}
main()
{
int pid = fork();
signal(SIGINT, sigkill);
signal(SIGQUIT, sigkill);
signal(SIGTERM, sigkill);
if(pid == 0) //IF CHILD
{
int ch;
me = "Child";
while(1)
{
ch = (rand() % 26) + 'A'; // limit range to ascii A-Z
printf("%c",ch);
fflush(stdout); // flush output buffer
sleep(2); // don't overwhelm
if (1 == getppid())
{
printf("=== CHILD EXIT SINCE PARENT DIED ===\n");
exit(0);
}
}
printf("==CHILD EXIT NORMAL==\n");
}
else //PARENT PROCESS
{
int ch;
if((ch = getchar())==27)
kill(pid, SIGINT);
//printf("==PARENT EXIT NORMAL (ch=%d)==\n", ch);
}
return(0);
}
In this program u will only need to press enter
after esc
char,because getchar()
is a blocking function.
Also u may remove or decrease sleep time for child process as ur need.

- 129
- 1
- 2
- 7
-
2Except `getchar` waits for input, so no random characters will be outputted while `getchar` waits for user to press [enter]. – James Morris Jun 06 '10 at 13:53