5

The program I am building runs in an infinite while loop with a switch case in it. I want to insert a while loop under each case and perform few operations in the loop, but the loop should exit as soon as a keyboard input is given. So that after taking the input from keyboard another case is run with the nested while loop in it, and the process continues.

The structure is:

while()//infinite loop
    {
    ...............      //operations
    ...............      //operations
    switch()
        {
        case 1:
        ...............    //operations
        ...............    //operations
        while()//infinite loop
             {
             ..............
             ..............
             exit if there is any input from keyboard
             }
        break;

        case 2:
        ...............    //operations
        ...............    //operations
        while()//infinite loop
             {
             ..............
             ..............
             exit if there is any input from keyboard
             }
        break;


        case n:
        ...............    //operations
        ...............    //operations
        while()//infinite loop
             {
             ..............
             ..............
             exit if there is any input from keyboard
             }
        break;
        }
  }

Is there any way to do it???

Nitish Sahay
  • 306
  • 5
  • 14
  • 2
    Are you asking how to get keyboard input, or how to break nested loops? – ugoren Jun 05 '13 at 10:24
  • Are you asking how to do raw input, so that waiting ends as soon as _any_ key that produces a char or control code is pressed, as opposed to being forced to press enter (after optionally inputting some text)? – blubberdiblub Jun 05 '13 at 10:29
  • Regardless of the solution to the keyboard problem, this function should be refactored. – unwind Jun 05 '13 at 10:30
  • I need to know how to break the nested loop with a keyboard input, so that, that input is fed to the case again as the outer while loop is run. What I am planning to do is, when a key is pressed and the nested while loop breaks, that key is written into a file. Then this key is taken as an input from the file, for the switch when the outer while loop runs again. – Nitish Sahay Jun 05 '13 at 10:33
  • 1
    Indeed, waiting could be done after the switch, once for all cases, instead of code duplication. – blubberdiblub Jun 05 '13 at 10:35
  • You could use the ncurses library and its `getch()` function. There are a few examples available on Stack Overflow. –  Jun 05 '13 at 10:45
  • Have You considered using threads ? One thread monitors keyboard, second is main loop ? Also You might try using signals, and for example wrap ctrl-c signal, which will set and clear flag in all loops ? – JosiP Jun 05 '13 at 10:58
  • On Linux, learning about [poll(2)](http://man7.org/linux/man-pages/man2/poll.2.html), perhaps [ncurses](http://www.gnu.org/software/ncurses/) or [readline](http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html) libraries could help you significantly. You still need to master the C programming language. There are good books for that. – Basile Starynkevitch Jun 05 '13 at 17:40

3 Answers3

6

Linux keyboard input is buffered, in order to catch a key that was hit on the fly you have to configure TERM IO.

Near the top of main() add a call to (see code below)

term_nonblocking();

to read a key that was pressed on the fly, without waiting for a carriage return (CR).

Code:

struct termios stdin_orig;  // Structure to save parameters

void term_reset() {
        tcsetattr(STDIN_FILENO,TCSANOW,&stdin_orig);
        tcsetattr(STDIN_FILENO,TCSAFLUSH,&stdin_orig);
}

void term_nonblocking() {
        struct termios newt;
        tcgetattr(STDIN_FILENO, &stdin_orig);
        fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // non-blocking
        newt = stdin_orig;
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &newt);

        atexit(term_reset);
}

Note: term_reset() will be called automatically when your program exits, (to reset the terminal parameters).

You can call the now non-blocking getchar() anywhere in your program to detect a key press

int i = getchar();

and check if a key was pressed:

if (i > 0) {
    // key was pressed, code in `i`
}

In your program for instance:

int key = 0;

while (... && key <= 0) {
   // ... 
   key = getchar();
}

Note: if you want the output to be unbuffered, call setbuf(stdout, NULL);

(comment from @stacey : getchar() may return 0 or -1 when no key is available)

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • 1
    You might want to `ungetc(i, stdin)` if you read a character so that it can be processed again by the outer loop. – Jonathan Leffler Jun 05 '13 at 14:11
  • @JonathanLeffler Thanks. This is valuable information indeed. – Déjà vu Jun 05 '13 at 15:07
  • 1
    Note that getchar() returns -1 or 0 if a key hasn't been pressed. I used `(key <= 0)` in my while condition to get around this. As it stands, the while loop ends immediately if nothing is pressed. – stanri Jul 05 '16 at 07:17
  • @stacey thanks, edited the answer as it may help other people. – Déjà vu Jul 05 '16 at 12:15
0

Thank you people.... even though my answer is a little out of box, I think ring0's approach is much better. Here is what I did.

  • Added code inside the outer while loop which allows the compiler to take argument for switch case from a file, say, data.dat
  • Added similar code in each of the nested while loops to continue if the variable retrieved from the data.dat is what is required to run the loop, if not, break.
  • Since in linux I can run processes in the back ground, I ran the main program in the background, and edited data.dat accordingly.

This method is working for me but will definitely try ring0's method too.

Nitish Sahay
  • 306
  • 5
  • 14
0

I had a similar desire for an old system that I have to maintain. I wanted to add a "quit" as well as provide some timing. I stripped out all proprietary code and left the shell of what I learned from this site. Thank you.

I ended up using the above example by ring0. I wanted to comment but I'm not at that level yet.

I also took some information from here by Ulf and Walter

I could not use fcntl because of a global variable that was used in our code that conflicted with something within fcntl and I did not want to figure out how to avoid that conflict.

Anyway, here is an example. It worked for me on Debian (older version). Your mileage may vary.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>

#define SLEEP  500000. /*usec*/

#include <termios.h>
struct termios stdin_orig;

void term_reset() {
  tcsetattr(STDIN_FILENO, TCSANOW, &stdin_orig);
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &stdin_orig);
}

void term_nonblocking() { 
  struct termios term;
  tcgetattr(STDIN_FILENO, &stdin_orig);
  term = stdin_orig;
  term.c_iflag |= IGNBRK;
  //term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
  term.c_lflag &= ~(ICANON);// | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);*/
  term.c_cc[VMIN] = 0;
  term.c_cc[VTIME] = 0;
  /*   fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // non-blocking */
  tcsetattr(STDIN_FILENO, TCSANOW, &term);
} 
// end mikes timing

int main (void)
{
  int key=0;
  // mikes test variables
  long mnloops = 0;
  double mttot = 0.0;//total time elapsed in difference between start-end.
  term_nonblocking();
  // end mikes test variables

  /*main loop*/
  do{
    //mikes timing
    puts("Start Do");
    time_t mnow;
    struct timeval mtv;
    struct timezone mtz;
    time(&mnow);
    gettimeofday(&mtv, &mtz);
    double startTime = (double)mnow + mtv.tv_usec/1e6;
    // end mikes timing
    usleep(SLEEP);
    puts("got hey");
    //mikes timing
    time(&mnow);
    gettimeofday(&mtv, &mtz);
    mttot += (((double)mnow + mtv.tv_usec/1e6) - startTime);
    mnloops++;
    key = getchar();
    // end mikes timing
  }while (1 && key != 'q');/* end of main loop*/
  term_reset();
  printf("DONE: average time per loop = %-10.4e\n",mttot/mnloops);
  return 0;
}
Community
  • 1
  • 1
evernoob
  • 103
  • 7