views:

270

answers:

3
+3  Q: 

Non-blocking stdio

I'm working on a program which will be taking in user input from the console as well as printfing out in a separate thread. I want to avoid situations where the user is halfway through typing something in and a printf comes along and prints itself at the cursor.

Is there a way to do non-blocking io in c from the console window? Ideally, capturing keypresses or something like that such that what the user types doesn't appear on the screen. I'm developing in Ubuntu, and it's best if I don't have to use things like ncurses.

+2  A: 

Here's an example of how to turn off echo from C, taken directly from an HP forum (and I haven't personally tested it):

Okay this should be a simple example of turning off echo:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>

#define STDIN_FDES 0

struct termios save;

int main(int argc, char *argv[])
{
int cc = 0;
char s_tmp[80],*p = NULL;
struct termios work;

cc = tcgetattr(STDIN_FDES,&save);
work = save;
work.c_lflag &= ~(ECHO);
cc = tcsetattr(STDIN_FDES,TCSANOW,&work);
(void) printf("\nEnter value: ");
(void) fflush(stdout);
p = fgets(s_tmp,sizeof(s_tmp),stdin);
if (p != NULL) (void) printf("Out -> %s\n",p);
cc = tcsetattr(STDIN_FDES,TCSANOW,&save);
return(cc);
}

NOTE: It is very important that you have signal handlers to catch SIGINT, SIGTERM, ... and reset the terminal using the original termios because the last tcsetattr() wins and this applies to the terminal device NOT simply the process. If you leave the process with echo off, it will be off in the shell as well.

Otherwise, if Bash is a suitable approach, apparently you can just do stty -echo.

Mark Rushakoff
+2  A: 

using termios you can disable terminal echoing:

#include <termios.h>

struct termios oflags, nflags;
tcgetattr(fileno(stdin), &oflags);
nflags = oflags;
nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL;

if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
    /* handle error */
}

then before exit (use atexit) you must restore the terminal:

if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) {
    /* handle error */
}
dfa
A: 

Turning off echo or using non-blocking I/O isn't the answer, if I understand your question correctly. Rather, you want to prevent a background thread from interrupting a user input thread, right?

For that, you'll need access to raw keypresses instead of line-buffered input. I don't know why you're allergic to ncurses or similar libraries; that's what they're for! I guess you could do it with termios or ioctl calls, if that's how you roll....

But to solve your multi-threaded TTY output problem, you could do this:

1) Create a mutex to control who can access the console

In the background thread, to output a message:

Grab the mutex; write the message; release the mutex; go back to sleep!

In the user input thread:

Grab the mutex when new input is detected. Keep exclusive access until the user hits enter, then release the mutex and give the background thread a chance to talk.

Does that help?

Jim Lewis
Take a look at cfmakeraw() for raw terminal mode. Be sure to save the termios structure before calling this function so you can restore it when you're done.
Rob Jones