C non-blocking keyboard input

I am trying to write a program in C (on Linux) that will quote until the user presses a key, but should not require a keystroke to continue each cycle.

Is there an easy way to do this? I suppose I can do this with select() , but that seems like a lot of work.

Alternatively, is there a way to catch pressing ctrl - c to clear before the program closes instead of a non-blocking io?

+68
c input linux asynchronous nonblocking
Jan 15 '09 at 23:28
source share
10 answers

As already mentioned, you can use sigaction to catch ctrl-c or select to capture any standard input.

Please note, however, that with the latter method, you also need to set TTY so that it works in a "different" mode, not in time. The last one by default - if you enter a line of text, it will not be sent to the running stdin program until you press enter.

You need to use the tcsetattr() function to disable ICANON mode and possibly also disable ECHO. From memory, you must also set the terminal to ICANON mode when the program exits!

Just for completeness, here is some code that I just hit (nb: no error check!) That installs Unix TTY and emulates the DOS <conio.h> kbhit() and getch() :

 #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/select.h> #include <termios.h> struct termios orig_termios; void reset_terminal_mode() { tcsetattr(0, TCSANOW, &orig_termios); } void set_conio_terminal_mode() { struct termios new_termios; /* take two copies - one for now, one for later */ tcgetattr(0, &orig_termios); memcpy(&new_termios, &orig_termios, sizeof(new_termios)); /* register cleanup handler, and set the new terminal mode */ atexit(reset_terminal_mode); cfmakeraw(&new_termios); tcsetattr(0, TCSANOW, &new_termios); } int kbhit() { struct timeval tv = { 0L, 0L }; fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds); return select(1, &fds, NULL, NULL, &tv); } int getch() { int r; unsigned char c; if ((r = read(0, &c, sizeof(c))) < 0) { return r; } else { return c; } } int main(int argc, char *argv[]) { set_conio_terminal_mode(); while (!kbhit()) { /* do some work */ } (void)getch(); /* consume the character */ } 
+50
Jan 15 '09 at 23:37
source share
β€” -

select() is too low for convenience. I suggest you use the ncurses to put the terminal in cbreak mode and delay mode, then call getch() , which will return ERR if the character is not ready:

 WINDOW *w = initscr(); cbreak(); nodelay(w, TRUE); 

At this point, you can call getch without blocking.

+15
Jan 16 '09 at 1:03
source share

On UNIX systems, you can use the sigaction call to register a signal handler for the SIGINT signal, which represents the Control + C key sequence. The signal handler can set a flag that will be checked in a loop, which will cause it to break.

+11
Jan 15 '09 at 23:31
source share

You probably want kbhit();

 //Example will loop until a key is pressed #include <conio.h> #include <iostream> using namespace std; int main() { while(1) { if(kbhit()) { break; } } } 

This may not work in all environments. A portable way would be to create a monitoring flow and set some flag to getch();

+6
Jan 15 '09 at 23:37
source share

The curses library can be used for this purpose. Of course, select() and signal handlers can be used to some extent.

+3
Jan 15 '09 at 23:36
source share

If you are happy, just catch Control-C, that's the job. If you really need non-blocking I / O but don’t want the curses library, another alternative is to move the lock, stock, and trunk to the AT & amp, T sfio library . This is a good C stdio drawing library, but more flexible, thread safe, and works better. (sfio means safe, fast I / O.)

+2
Jan 17 '09 at 3:08
source share

Another way to get non-blocking keyboard input is to open the device file and read it!

You must know the device file you are looking for, one of / dev / input / event *. You can run cat / proc / bus / input / devices to find the device you need.

This code works for me (runs as administrator).

  #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <linux/input.h> int main(int argc, char** argv) { int fd, bytes; struct input_event data; const char *pDevice = "/dev/input/event2"; // Open Keyboard fd = open(pDevice, O_RDONLY | O_NONBLOCK); if(fd == -1) { printf("ERROR Opening %s\n", pDevice); return -1; } while(1) { // Read Keyboard Data bytes = read(fd, &data, sizeof(data)); if(bytes > 0) { printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code); } else { // Nothing read sleep(1); } } return 0; } 
+2
Apr 23 '14 at 19:55
source share

There is no portable way to do this, but select () can be a good way. See http://c-faq.com/osdep/readavail.html for more possible solutions.

+1
Jan 15 '09 at 23:33
source share

You can do this using the select command:

  int nfds = 0; fd_set readfds; FD_ZERO(&readfds); FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */ while(1) { /* Do what you want */ int count = select(nfds, &readfds, NULL, NULL, NULL); if (count > 0) { if (FD_ISSET(0, &readfds)) { /* If a character was pressed then we get it and exit */ getchar(); break; } } } 

Not too much work: D

0
04 Feb '16 at 8:10
source share

Here you can do it for you. You need termios.h , which comes with POSIX systems.

 #include <termios.h> void stdin_set(int cmd) { struct termios t; tcgetattr(1,&t); switch (cmd) { case 1: t.c_lflag &= ~ICANON; break; default: t.c_lflag |= ICANON; break; } tcsetattr(1,0,&t); } 

Abort this: tcgetattr gets the current terminal information and stores it in t . If cmd is 1, the local input flag at t set to non-blocking input. Otherwise, it is reset. Then tcsetattr changes the standard input to t .

If you do not use reset standard input at the end of your program, you will have problems in your shell.

0
Jun 17 '16 at 2:12
source share



All Articles