5

Please find the section of code below. I want to print the current value of the variable continuously in a loop. And the loop has to be terminated once I hit escape key. Here the problem is that the execution stops at the getchar function. But I want it to continue and print the value of variable until I hit the escape button.

do
{
     vUpdateVariable();        // routine to update the current value of variable
     printf("Value is %f\r", fVariable); 
     ucKey = getchar();
     usleep(1000);
}while (ucKey != 0x1B);  
Vality
  • 6,577
  • 3
  • 27
  • 48
SilentCat
  • 75
  • 1
  • 7
  • 1
    getchar is a blocking call. Is this on Linux or Windows or some other OS? On Windows, use _kbhit. On Linux, you need to setup something with ioctl or modify the terminal attributes. – cup Feb 20 '14 at 09:33
  • For Linux, see [here](http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input). For Windows, see [here](http://stackoverflow.com/questions/3643738/how-to-read-available-input-without-blocking-on-windows?rq=1). – herohuyongtao Feb 20 '14 at 09:37
  • 1
    @starsplusplus Bad duplicate. Don't like a duplicate in C++ when the OP specifies C. – Lundin Feb 20 '14 at 09:43
  • I am working in Linux. I shall try with modifications in Terminal attributes. – SilentCat Feb 20 '14 at 09:52
  • @Lundin Sorry, missed that. That the URL said "C" didn't help! Will delete. – starsplusplus Feb 20 '14 at 09:59
  • 1
    @herohuyongtao I would disagree regarding the duplicate, that question is asking for a specific implementation, or reference to a syscall. This question is asking for any valid solution to fix his problem, it could be solved by asynchronous input but does not have to be (see my answer) – Vality Feb 20 '14 at 11:01

2 Answers2

2

There are a wide range of ways to do this including that mentioned here however my personal favorite is to use a fork and signal. This is more efficient than non blocking ops (does not waste system calls and thus context switches) on every loop iteration.

Effectively you fork a worker process to perform the loop, install a signal handler for say USR1 into it then have your parent process wait for a keypress blocking, once it receives the wanted key it can send the USR1 signal to the other process which will cause the signal handler to clean it up and terminate.

Please see these for more info:

I shall also note that this solution works for effectively any blocking situation which you could fix using non-blocking IO and will usually save some CPU time, albeit costing a little memory and CPU when creating the fork.

Community
  • 1
  • 1
Vality
  • 6,577
  • 3
  • 27
  • 48
  • 1
    is it necessary to fork a new process. What about threads? To create a thread to do the print the fVariable and vUpdateVariable function. And the main waits for the escape key to be pressed. Once the key is pressed the thread can be kill in the main functions. Is it a good solution? Since threads are much more light weight. – SilentCat Feb 20 '14 at 11:36
  • 1
    True actually, you are correct about threads, I avoided them because they are more complex to set up (IMO) for a simple situation like this. However if you think they will work better (which is likely true) or be faster I am happy to add a note to the answer, or you could create your own if you prefer. Actually I would love to see a benchmark of the two approaches, if I get time I may try it. – Vality Feb 20 '14 at 11:44
  • Okay. That sounds good. I shall try with both the ways and keep posted. – SilentCat Feb 20 '14 at 11:52
0

I got it worked with the code below. For continuously printing the variable. The stderr had to be used. stdout will flush the buffer when the programmer explicitly asks for it or when it is most convenient, stderr writes the message immediately. Due to the canonical mode of your terminal, you need to hit enter to confirm your user input. Canonical mode has to be disable for non blocking execution of the code. Canonical mode means it always wait for enter to confirms the user input. If that is not your case, nonblock is a function to cater that. I dint figure it out myself. Got the code and explanation from other forums.

#include<stdio.h>
#include<curses.h>
#include<unistd.h>
#include <termios.h>
#include <sys/time.h>

#define NB_ENABLE 0x01
#define NB_DISABLE 0x00

int kbhit()
{
    struct timeval tv;
    fd_set fds;
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
    select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
    return FD_ISSET(STDIN_FILENO, &fds);
}

void nonblock(int state)
{
     struct termios ttystate;
     tcgetattr(STDIN_FILENO, &ttystate); //get the terminal state

     if (state==NB_ENABLE)
     {
          //turn off canonical mode
          ttystate.c_lflag &= ~ICANON;
          //minimum of number input read.
          ttystate.c_cc[VMIN] = 1;
     }
     else if (state==NB_DISABLE)
     {
           //turn on canonical mode
          ttystate.c_lflag |= ICANON;
     }
     //set the terminal attributes.
     tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
 }
 int main()
 {
      char c,i;
      while(c != 27)
      {
           usleep(10000);
           vUpdateVariable();        // routine to update the current value of variable
           printf(stderr,"Value is %f\r", fVariable);            
           i=kbhit();
           c=fgetc(stdin);
       }
       nonblock(NB_DISABLE);
       return 0;
 }
SilentCat
  • 75
  • 1
  • 7