I have a custom _kbhit() function used for non-blocking character input. _kbhit() function uses select() to check if stdin is ready to read. But finally I have stdin ready if only I pressed RETURN. How to make it work when I press any key?
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &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);
}
static char _getch(void) {
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 (1) {
if (_kbhit()) {
inputbuffer[inputlen] = _getch();
if (inputbuffer[inputlen] >= 0x20 && inputbuffer[inputlen] < 0x7F) {
putchar(inputbuffer[inputlen]);
inputlen++;
}
}
}
}
I expected that this code will output echo, but it outputs entire string just after I press RETURN.
I think the problem is that - despite your initialization of the terminal attributes using cfmakeraw()
- stdout
is still in line-buffered mode.
Try setting standard output to unbuffered mode.
setbuf(stdout, NULL);
which is equivalent to
setvbuf(stdout, NULL, _IONBF, 0);
This avoids the need to call fflush(stdout);
after every putchar(inputbuffer[inputlen]);
.
There may be a way of accomplishing this using tcsetattr()
instead of setvbuf()
, buf I am not familiar with it.