How to use select() to read input from keyboard in C - c

I am trying to use select() to read keyboard input and I got stuck in that I do not know how to read from keyboard and use a file descriptor to do so. I've been told to use STDIN and STDIN_FILENO to approach this problem but I am still confused.
How can I do it?

Youre question sounds a little confused. select() is used to block until input is available. But you do the actual reading with normal file-reading functions (like read,fread,fgetc, etc.).
Here's a quick example. It blocks until stdin has at least one character available for reading. But of course unless you change the terminal to some uncooked mode, it blocks until you press enter, when any characters typed are flushed into the file buffer (from some terminal buffer).
#include <stdio.h>
#include <sys/select.h>
int main(void) {
fd_set s_rd, s_wr, s_ex;
FD_ZERO(&s_rd);
FD_ZERO(&s_wr);
FD_ZERO(&s_ex);
FD_SET(fileno(stdin), &s_rd);
select(fileno(stdin)+1, &s_rd, &s_wr, &s_ex, NULL);
return 0;
}

As it was already said, by using select you can just monitor e.g. stdin to check if the input data is already available for reading or not. If it is available, you can then use e.g. fgets to safely read input data to some buffer, like shown below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
fd_set rfds;
struct timeval tv;
int retval, len;
char buff[255] = {0};
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
if (retval == -1){
perror("select()");
exit(EXIT_FAILURE);
}
else if (retval){
/* FD_ISSET(0, &rfds) is true so input is available now. */
/* Read data from stdin using fgets. */
fgets(buff, sizeof(buff), stdin);
/* Remove trailing newline character from the input buffer if needed. */
len = strlen(buff) - 1;
if (buff[len] == '\n')
buff[len] = '\0';
printf("'%s' was read from stdin.\n", buff);
}
else
printf("No data within five seconds.\n");
exit(EXIT_SUCCESS);
}

Perhaps, you want the way to peek keyboard input on "WINDOWS"?
On windows, it can't get result from select() for STDIN. You should use PeekConsoleInput().
And use handle of stdin like following.
hStdin = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE, ...
stdin may become pipe input. if so, you don't get any keyboard input.
P.S. If you don't ask about Windows, Sorry much.

Related

How to use a pseudo-terminal returned from posix_openpt?

I'm trying to use posix_openpt on Mac. The issue I'm seeing is that I get a file descriptor back from posix_openpt. I use the file descriptor for reading and create a copy using dup for writing. The issue I'm running into is that when I write to the master file descriptor, I read that data back out from the master. So no data ends up at the slave. I confirmed this by using posix_spawnp to run a program with stdin/stdout/stderr set to the slave file. The program hangs indefinitely waiting for input. Here is my code (note, all error handling was removed for legibility):
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
posix_spawn_file_actions_addopen(&fd_actions, STDIN_FILENO, slave_filename, O_RDONLY, 0644);
posix_spawn_file_actions_addopen(&fd_actions, STDOUT_FILENO, slave_filename, O_WRONLY, 0644);
posix_spawn_file_actions_adddup2(&fd_actions, STDOUT_FILENO, STDERR_FILENO);
pid_t pid;
posix_spawnp(&pid, "wc", &fd_actions, NULL, NULL, NULL);
int master_fd_write = dup(master_fd);
char *data = "hello world";
write(master_fd_write, data, strlen(data));
close(master_fd_write);
char buffer[1024];
read(master_fd, buffer, 1024); // <- Issue Here
// buffer now contains hello world. It should contain the output of `wc`
(Note: The above was only tested on Linux; I don't have a Mac to work on, but I have no reason to believe it's any different in the details here.)
There are several problems with your code:
At least on Linux, calling posix_spawn() with a null pointer causes a crash. You need to provide all the arguments. Even if Macs accept it the way you have it, doing this is a Good Idea.
Next, wc reading from standard input will wait until an attempt to read more data gives an End Of File condition before it prints out the statistics it gathers; your code doesn't do this. With a pty, if you write a specific byte (Typically with the value 4, but it can be different, so best to use what the terminal says instead of hardcoding it) to it, the terminal driver will recognize that as signalling EOF without having to close the master like you would when using a pipe (Making it impossible to read the output of wc).
Second, the terminal's default settings include echoing the input; that's what you're reading.
A cleaned up version that addresses these issues and more (Like yours, with most error checking omitted; real code should be checking all these functions for errors):
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <spawn.h>
#include <termios.h>
#include <unistd.h>
#include <wait.h>
int main(void) {
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
//printf("slave pty filename: %s\n", slave_filename);
// Open the slave pty in this process
int slave_fd = open(slave_filename, O_RDWR);
// Set up slave pty to not echo input
struct termios tty_attrs;
tcgetattr(slave_fd, &tty_attrs);
tty_attrs.c_lflag &= ~ECHO;
tcsetattr(slave_fd, TCSANOW, &tty_attrs);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
// Use adddup2 instead of addopen since we already have the pty open.
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDIN_FILENO);
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDOUT_FILENO);
// Also close the master and original slave fd in the child
posix_spawn_file_actions_addclose(&fd_actions, master_fd);
posix_spawn_file_actions_addclose(&fd_actions, slave_fd);
posix_spawnattr_t attrs;
posix_spawnattr_init(&attrs);
pid_t pid;
extern char **environ;
char *const spawn_argv[] = {"wc" , NULL};
posix_spawnp(&pid, "wc", &fd_actions, &attrs, spawn_argv, environ);
close(slave_fd); // No longer needed in the parent process
const char *data = "hello world\n";
ssize_t len = strlen(data);
if (write(master_fd, data, len) != len) {
perror("write");
}
// Send the terminal's end of file interrupt
cc_t tty_eof = tty_attrs.c_cc[VEOF];
if (write(master_fd, &tty_eof, sizeof tty_eof) != sizeof tty_eof) {
perror("write EOF");
}
// Wait for wc to exit
int status;
waitpid(pid, &status, 0);
char buffer[1024];
ssize_t bytes = read(master_fd, buffer, 1024);
if (bytes > 0) {
fwrite(buffer, 1, bytes, stdout);
}
close(master_fd);
return 0;
}
When compiled and run, outputs
1 2 12
There are two problems with this code.
First, you are seeing "hello world" on master_fd because by default terminals echo. You need to set the terminal to raw mode to suppress that.
Second, wc won't output anything until it sees an EOF, and it will not see an EOF until you close the master. Not just master_fd_write mind you, but all copies of master_fd, including master_fd itself. However, once you close the master, you cannot read from it.
Choose some other program that wc to demonstrate the functionality of posix_openpt.
Edit: It is possible to raise the end-of-file condition on the slave without closing the master by writing ^D (EOT, ascii 4).

Getting input from user. Shell

I am writing my own simple shell and currently I'm thinking of getting input ( command ) from user.
I wrote a following prototype:
while(1) {
printf("gsh> ");
fflush(stdout);
total_len = 0;
do {
len = read(0, buffer, MAX_LENGTH_OF_COMMAND-total_len -1);
total_len+= len;
} while( buffer[total_len-1] != '\n');
buffer[total_len]='\0';
parse(buffer);
}
And this soultion seems me to be best, but I am not sure. So, I am asking for correct and recommend/advice me something.
Thanks in advance.
You may rather use getchar() so you can be able to catch keys like up and down arrow (usually useful for shell history) that generate more than one character when you press it. You may also want to make your terminal as raw to get non blocking inputs.
#include <termios.h>
#include <unistd.h>
int main()
{
struct termios oldt;
struct termios newt;
tcgetattr(0, &oldt);
memcpy(&newt, &oldt, sizeof(newt));
cfmakeraw(&newt);
tcsetattr(0, TCSANOW, &newt);
/* your read function ...*/
/* before exiting restore your term */
tcsetattr(0, TCSANOW, &oldt);
}
A good way to create a custom prompt is using read. There is multiple ways so there is always a cleaner / better solution. But here is mine:
while ((fd = read(0, buff, BUFF_SIZE) > 0) {
if (fd == BUFF_SIZE)
// Command to big, handle this as you want to
buff[fd - 1] = '\0';
// Do what you want with your buff
}
Of course this solution has a max buffer size. You would need to wrap the read inside anoter function and use malloc to allocate the good size.

Nonblocking Get Character

Platform: Linux 3.2.0 x86 (Debian 7)
Compiler: GCC 4.7.2 (Debian 4.7.2-5)
I am writing a function that reads a single character from stdin if a character is already present in stdin. If stdin is empty the function is suppose to do nothing and return -1. I googled nonblocking input and was pointed to poll() or select(). First I tried to use select() but I could not get it to work so I tried poll() and reached the same conclusion. I am not sure what these functions do exactly but from what I understand of poll()'s documentation if I call it like so:
struct pollfd pollfds;
pollfds = STDIN_FILENO;
pollfds.events = POLLIN;
poll(pollfds, 1, 0);
if(pollfds.revents & POLLIN) will be true if "Data other than high-priority data may be read without blocking.". But poll() always times out in my test situation. How I test the function could be the problem but the functionality I want is exactly what I am testing for. Here is the function currently and the test situation as well.
#include <poll.h>
#include <stdio.h>
#include <unistd.h>
int ngetc(char *c)
{
struct pollfd pollfds;
pollfds.fd = STDIN_FILENO;
pollfds.events = POLLIN;
poll(&pollfds, 1, 0);
if(pollfds.revents & POLLIN)
{
//Bonus points to the persons that can tell me if
//read() will change the value of '*c' if an error
//occurs during the read
read(STDIN_FILENO, c, 1);
return 0;
}
else return -1;
}
//Test Situation:
//Try to read a character left in stdin by an fgets() call
int main()
{
int ret = 0;
char c = 0;
char str[256];
//Make sure to enter more than 2 characters so that the excess
//is left in stdin by fgets()
fgets(str, 2, stdin);
ret = ngetc(&c);
printf("ret = %i\nc = %c\n", ret, c);
return 0;
}
You're doing IO incorrectly, the POSIX manual and all other related documentation explicitly says never to mix IO done on FILE *s and file descriptors. You have very blatantly broken this rule. This rule is in place because FILE *s use buffering an this means that after a call to fgets there will be nothing left for read to get because fgets already read all pending data into a buffer that is kept in the FILE * structure.
So since there's no way to check if an ISO C IO method will block, we have to use file descriptors only.
Since we know that STDIN_FILENO is just the number 0, we can use
fcntl (0, F_SETFL, O_NONBLOCK);
this will turn all reads on file descriptor 0 to non-blocking mode, if you want to use a different file descriptor so that you can leave 0 alone then just use dup to duplicate it.
This way, you can stay away from poll completely and implement ngetc as
ssize_t
ngetc (char *c)
{
return read (0, c, 1);
}
or better yet, a macro
#define ngetc(c) (read (0, (c), 1))
Thus you get a simple implementation for what you're looking for.
Edit: If you are still worried about the terminal buffering the input, you can always change the terminal's settings, see How to disable line buffering of input in xterm from program? for more information on how to do this.
Edit: The reason that one could not use fgetc instead of read is for the same reason that using fgets won't work. When one of the FILE * IO functions is run, it reads all the data from the associated file descriptor. But once that happens, poll will never return because it's waiting on a file descriptor that's always empty, and the same thing will happen with read. Thus, I suggest that you follow the advice of the documentation and never mix streams (IO using fgets, fgetc, etc.) and file descriptors (IO using read, write, etc.)
There are two problems in your code.
According to manual of poll, assigning 0 to timeout will return immediately
If the value of timeout is 0, poll() shall return immediately. If the value of timeout is -1, poll() shall block until a requested event occurs or until the call is interrupted.
fgets does not do what you expect, it is from stdio library and will buffer reads. Suppose you entered 3 letters and press enter, after fgets, the third letter won't be available to poll.
So comment out the fgets line and assign -1 to timeout in poll, and run it again to see if that's what you want.
I did not get the expected behavior with the answer above, and I actually had to take into account this answer as well
which set the TTY in non canonical mode.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
int main(int argc, char *argv[])
{
struct termios t;
tcgetattr(0, &t);
t.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &t);
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
printf("Starting loop (press i or q)...\n");
for (int i = 0; ; i++) {
char c = 0;
read (0, &c, 1);
switch (c) {
case 'i':
printf("\niteration: %d\n", i);
break;
case 'q':
printf("\n");
exit(0);
}
}
return 0;
}

Making stdin non-blocking

I have an exercise where I am required to print a file slowly (1 second intervals) until the file ends, unless the user types a character.
So far, the program outputs the file in one second intervals which is great, but when I type a character, nothing happens. My guess is that I am using select wrong somehow.
This is the final program I ended up submitting.
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
FILE* infile;
char str[100];
fd_set readset;
struct timeval tv;
// open a file
if((infile = fopen("infile", "r")) == NULL)
{
(void)printf("Couldn't open the file\n");
exit(1);
}
// file was opened successfully
else
{
// while we are not at the end of a file
while(fgets(str, 100, infile) != NULL)
{
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
// set the time value to 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
select(fileno(infile)+1, &readset, NULL, NULL, &tv);
// the user typed a character so exit
if(FD_ISSET(fileno(stdin), &readset))
{
fclose(infile);
exit(0);
}
// the user didn't type a character so print the next line
else
{
fgets(str, 100, stdin);
puts(str);
}
}
// clean up
fclose(infile);
}
// report success
return 0;
}
Thanks for the help!
This is a working version, using tcgetattr/tcsetattr:
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
int main(void) {
FILE* infile;
char str[100];
fd_set readset;
struct timeval tv;
struct termios ttystate, ttysave;
// open a file
if((infile = fopen("infile", "r")) == NULL)
{
(void)printf("Couldn't open the file\n");
exit(1);
}
// file was opened successfully
//get the terminal state
tcgetattr(STDIN_FILENO, &ttystate);
ttysave = ttystate;
//turn off canonical mode and echo
ttystate.c_lflag &= ~(ICANON | ECHO);
//minimum of number input read.
ttystate.c_cc[VMIN] = 1;
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
// while we are not at the end of a file
while(fgets (str, 100, infile))
{
// set the time value to 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
select(fileno(stdin)+1, &readset, NULL, NULL, &tv);
// the user typed a character so exit
if(FD_ISSET(fileno(stdin), &readset))
{
fgetc (stdin); // discard character
break;
}
// the user didn't type a character so print the next line
else
{
puts(str);
// not needed: sleep(1);
}
}
// clean up
fclose(infile);
ttystate.c_lflag |= ICANON | ECHO;
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttysave);
// report success
return 0;
}
The sleep(1); isn't needed anymore.
The terminal is buffering lines. It doesn't send text to the program until you press the Enter key. There might be a way to disable terminal line buffering, but I imagine it is beyond the scope of your assignment.
It stops when you press Enter. However, it doesn't quit immediately. That's something you'll want to fix. Get rid of that sleep(1).
Now your program spams text! You gave select a timeout of one second, didn't you?
// set the time value to 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
The reason the timeout doesn't stick is because select is modifying the timeout value. From the man page:
On Linux, select() modifies timeout to reflect the amount of time not
slept; most other implementations do not do this. (POSIX.1-2001
permits either behavior.) This causes problems both when Linux code
which reads timeout is ported to other operating systems, and when
code is ported to Linux that reuses a struct timeval for multiple
select()s in a loop without reinitializing it. Consider timeout to be
undefined after select() returns.
You will need to initialize the timeval before every call to select, not just once at the beginning of the program.
You'd want to make your program multithreaded. Create a thread that prints out the file every 1 second interval, and your main thread would be getting input from stdin, then signal the other thread to stop printing whenever you get the input
Part of your problem is that you are using sleep(1) which will cause that line to take a full second to execute. If the user types a character, they will have to wait up to a full second before your program responds. So even once you get the non-blocking portion working you will still have issues.
The solution is to use nanosleep or usleep to pause the program for less than 1 second. My recommendation would be to sleep for 1/100 of a second *using one of those functions) and check for user key presses every time. On the 100th time, output the next portion of the file. That way the file still goes the right speed, but the user can stop it whenever they want and the program will respond to their command very quickly.

How do you do non-blocking console I/O on Linux in C?

How do you do nonblocking console IO on Linux/OS X in C?
I want to add an example:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[20];
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
sleep(4);
int numRead = read(0, buf, 4);
if (numRead > 0) {
printf("You said: %s", buf);
}
}
When you run this program you have 4 seconds to provide input to standard in. If no input found, it will not block and will simply return.
2 sample executions:
Korays-MacBook-Pro:~ koraytugay$ ./a.out
fda
You said: fda
Korays-MacBook-Pro:~ koraytugay$ ./a.out
Korays-MacBook-Pro:~ koraytugay$
Like Pete Kirkham, I found cc.byexamples.com, and it worked for me. Go there for a good explanation of the problem, as well as the ncurses version.
My code needed to take an initial command from standard input or a file, then watch for a cancel command while the initial command was processed. My code is C++, but you should be able to use scanf() and the rest where I use the C++ input function getline().
The meat is a function that checks if there is any input available:
#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>
// cc.byexamples.com calls this int kbhit(), to mirror the Windows console
// function of the same name. Otherwise, the code is the same.
bool inputAvailable()
{
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return (FD_ISSET(0, &fds));
}
This has to be called before any stdin input function When I used std::cin before using this function, it never returned true again. For example, main() has a loop that looks like this:
int main(int argc, char* argv[])
{
std::string initialCommand;
if (argc > 1) {
// Code to get the initial command from a file
} else {
while (!inputAvailable()) {
std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl;
sleep(1);
}
std::getline(std::cin, initialCommand);
}
// Start a thread class instance 'jobThread' to run the command
// Start a thread class instance 'inputThread' to look for further commands
return 0;
}
In the input thread, new commands were added to a queue, which was periodically processed by the jobThread. The inputThread looked a little like this:
THREAD_RETURN inputThread()
{
while( !cancelled() ) {
if (inputAvailable()) {
std::string nextCommand;
getline(std::cin, nextCommand);
commandQueue.lock();
commandQueue.add(nextCommand);
commandQueue.unlock();
} else {
sleep(1);
}
}
return 0;
}
This function probably could have been in main(), but I'm working with an existing codebase, not against it.
For my system, there was no input available until a newline was sent, which was just what I wanted. If you want to read every character when typed, you need to turn off "canonical mode" on stdin. cc.byexamples.com has some suggestions which I haven't tried, but the rest worked, so it should work.
You don't, really. The TTY (console) is a pretty limited device, and you pretty much don't do non-blocking I/O. What you do when you see something that looks like non-blocking I/O, say in a curses/ncurses application, is called raw I/O. In raw I/O, there's no interpretation of the characters, no erase processing etc. Instead, you need to write your own code that checks for data while doing other things.
In modern C programs, you can simplify this another way, by putting the console I/O into a thread or lightweight process. Then the I/O can go on in the usual blocking fashion, but the data can be inserted into a queue to be processed on another thread.
Update
Here's a curses tutorial that covers it more.
I bookmarked "Non-blocking user input in loop without ncurses" earlier this month when I thought I might need non-blocking, non-buffered console input, but I didn't, so can't vouch for whether it works or not. For my use, I didn't care that it didn't get input until the user hit enter, so just used aio to read stdin.
Here's a related question using C++ -- Cross-platform (linux/Win32) nonblocking C++ IO on stdin/stdout/stderr
Another alternative to using ncurses or threads is to use GNU Readline, specifically the part of it that allows you to register callback functions. The pattern is then:
Use select() on STDIN (among any other descriptors)
When select() tells you that STDIN is ready to read from, call readline's rl_callback_read_char()
If the user has entered a complete line, rl_callback_read_char will call your callback. Otherwise it will return immediately and your other code can continue.
Let`s see how it done in one of Linux utilites. For example, perf/builtin-top.c sources (simplified):
static void *display_thread(void *arg)
{
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
struct termios save;
set_term_quiet_input(&save);
while (!done) {
switch (poll(&stdin_poll, 1, delay_msecs)) {
...
}
}
tcsetattr(0, TCSAFLUSH, &save);
}
So, if you want to check if any data available, you can use poll() or select() like this:
#include <sys/poll.h>
...
struct pollfd pfd = { .fd = 0, .events = POLLIN };
while (...) {
if (poll(&pfd, 1, 0)>0) {
// data available, read it
}
...
}
In this case you will receive events not on each key, but on whole line, after [RETURN] key is pressed. It's because terminal operates in canonical mode (input stream is buffered, and buffer flushes when [RETURN] pressed):
In canonical input processing mode, terminal input is processed in
lines terminated by newline ('\n'), EOF, or EOL characters. No input
can be read until an entire line has been typed by the user, and the
read function (see Input and Output Primitives) returns at most a
single line of input, no matter how many bytes are requested.
If you want to read characters immediately, you can use noncanonical mode. Use tcsetattr() to switch:
#include <termios.h>
void set_term_quiet_input()
{
struct termios tc;
tcgetattr(0, &tc);
tc.c_lflag &= ~(ICANON | ECHO);
tc.c_cc[VMIN] = 0;
tc.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &tc);
}
Simple programm (link to playground):
#include <stdio.h>
#include <unistd.h>
#include <sys/poll.h>
#include <termios.h>
void set_term_quiet_input()
{
struct termios tc;
tcgetattr(0, &tc);
tc.c_lflag &= ~(ICANON | ECHO);
tc.c_cc[VMIN] = 0;
tc.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &tc);
}
int main() {
struct pollfd pfd = { .fd = 0, .events = POLLIN };
set_term_quiet_input();
while (1) {
if (poll(&pfd, 1, 0)>0) {
int c = getchar();
printf("Key pressed: %c \n", c);
if (c=='q') break;
}
usleep(1000); // Some work
}
}
Not entirely sure what you mean by 'console IO' -- are you reading from STDIN, or is this a console application that reads from some other source?
If you're reading from STDIN, you'll need to skip fread() and use read() and write(), with poll() or select() to keep the calls from blocking. You may be able to disable input buffering, which should cause fread to return an EOF, with setbuf(), but I've never tried it.

Resources