Reputation: 1937
I haven't found a post addressing a similar scenario so far:
In a command-line unix program for sonification of certain floating point data sets, I have a main()
which either reads from a file whose name is given as program argumant or stdin
redirection ( < )
or data piped from another program ( | )
, depending on argc
.
For as long as argv[1]
is a file name, I can manage gently stopping the execution by hitting 'enter' key, which programmatically sets a condition to fade out the signal, cleanup and regularly exit the audio subroutine and terminate the program. I achieve it by calling a function for non-blocking kbhit()
. Definition:
int kbhit(void)
{
struct timeval tv;
fd_set read_fd;
tv.tv_sec = 0;
tv.tv_usec= 0;
FD_ZERO(&read_fd);
FD_SET(0, &read_fd);
if(select(1, &read_fd, NULL, NULL, &tv) == -1)
return 0;
if(FD_ISSET(0, &read_fd))
return 1;
return 0;
}
However, if input is being redirected or piped, kbhit()
then prevents from execution by stopping the playback the moment it has started, since it returns 1
. I don't know how to deal with it, so I am using SIGINT, which I don't find so elegant. Here are the code snippets:
//... ...
bool stop = false; //global variable
//... ...
if(argc == 1) stdr = true;//flag: reading from redirected or piped stdin (simplified)
//code for reading from file or stdin, using fscanf() ...
//code for initializing and starting audio process ...
//loop, during audio callback on a separate thread ...
while((player.totalFrames < player.totalSize) && !stop){
if(!stdr){ //( = reading from file)
if (kbhit()){
stop = true;
break;
}
}else{ //( = reading from redirected stdin)
if (signal(SIGINT, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGINT\n");
}
}
if(stop){ … }//code for gently fading the signal and stopping the audio process
//cleanup and termination...
//... ...
And here is the standard signal handler which sets stop
condition to true
:
void sig_handler(int s)
{
if (s == SIGINT){
if(debug)printf("\ncaught SIGINT\n");
stop = true;
}
}
Is there a more elegant way to:
how would a code allowing for such a message from coputer keyoard (during redirection or piping) look like? Could the arguments inside kbhit()
be rearranged somehow, or one has to go in a completely different direction. I have no idea which?
If this is possible, I would kindly appreciate a suggestion. Thanks in advance!
Upvotes: 0
Views: 132
Reputation: 94
Your problem is that, when reading data from redirection, you're trying to use both the file AND your keyboard. stdin isn't your keyboard anymore, it is the file, and thus you cannot read data from your keyboard (like the "enter" key pressed).
If I'm not mistaken, your only two options are signals and opening a new file descriptor to the keyboard input (the TTY).
Upvotes: 1
Reputation: 58627
If you want an interactive TTY user to be able to stop a program using an input character (not a signaling character which causes the TTY driver to deliver a signal), and that program must be redirected, then your only resort is to explicitly open a file descriptor to the TTY device (via /dev/tty
) and monitor that for input.
Your kbhit
function should probably take an int fd
parameter, using which you give it the open TTY file descriptor that it should poll.
If you want a single character input from a TTY to be immediately available to the program (even if that character isn't a newline), then you have to put the TTY into "raw mode", or at least partially: at the very least, you have to disable "canonical input processing" via negating the ICANON
flag in the c_iflag
numeric field in the struct termios
structure, and ensure that c_cc[VTIME]
and c_cc[VMIN]
are set to 0 and 1, respectively. Look up tcgetattr
and tcsetattr
. You probably want to disable the signaling characters, and disable echoing and other things. Some platforms have a cfmakeraw
function which tweaks a struct termios
object in the right ways to bring about a very raw mode without having to fiddle with any of the termios
flags.
In canonical input processing mode, a TTY won't make input available to the process (and won't select positive under select
for input) until a complete line of input is received.
Upvotes: 1