1

I'm trying to get a while loop to iterate until a key, ideally enter, is pressed.

Specifically, what I want is for a value to continually update and be visible to the user until he presses enter.

Here is the basic code I'm working with, which doesn't work as I'd like it to

while(1){       
    printf("Press Enter to Continue\n");    
    printf("Value to Update: %f\n",value_to_update); 
    usleep(10000);      
    system("clear"); 
    while(getchar() != '\n'){
        continue; 
    }       
}; 

Thanks. Happy to clarify anything I've said or answer other questions.

I'm not sure how to explain exactly what I'm looking for. I'll try two ways:

1) I want it to do what the following code does, except stop when the user presses enter:

while(1){           
    printf("Value to Update: %f\n",value_to_update); 
    usleep(10000);      
    system("clear"); 
};

2) I'll enumerate the steps

1) Print the value to update to the screen

2) Wait for N microseconds

3) Did the User Press enter?

3.False) If No: Clear the screen, Go to step 1

3.True) If Yes: Break from loop

I guess this is way way harder than I thought it would be.

GeneralPancake
  • 535
  • 2
  • 6
  • 15
  • 1
    Can you be more specific on what you mean by "doesn't work as I'd like it to"? What is the output that you are seeing? – hopia Jan 18 '19 at 22:28
  • Post input used and output seen, output desired. – chux - Reinstate Monica Jan 18 '19 at 22:33
  • If you want this to operate in the background while you update the display in the foreground, you need to user OS-specific code, it can't be done in standard C. – Barmar Jan 18 '19 at 22:38
  • _Side note:_ I'd move your `printf` calls to _after_ the screen clear. Otherwise, they'll be output and then the screen is cleared so fast, that the user probably won't see them and will be left with a blank screen, wondering what to do. – Craig Estey Jan 18 '19 at 22:42
  • @Barmar Should be working on Linux? – GeneralPancake Jan 18 '19 at 22:44
  • Are you on WinX or POSIX (e.g. linux, BSD, macOS)? On POSIX, `getchar` will freeze, waiting for input. You'll need to issue an `ioctl` call to the TTY layer to put the input into raw mode, so you can poll it. Or, you'll need to do a `select` call on the `stdin` file descriptor (i.e. `STDIN_FILENO`) to poll for a ready char and then do `read` (vs. `getchar`). You could also use `fcntl` to put `STDIN_FILENO` (using `O_NONBLOCK`) to put the input into a non-blocking mode, so you can do the `read` directly. – Craig Estey Jan 18 '19 at 22:47
  • `getchar()` is not your friend here, because it will block until it can return a character. Moreover, the terminal will normally send data to your program a line at time, so if the user types any keys other than then you'll have to go through multiple iterations of your loop before you get to the newline. For something like this on Linux, you probably want to use I/O routines from [ncurses](https://www.gnu.org/software/ncurses/) or a similar library. – John Bollinger Jan 18 '19 at 22:48
  • Would you guys consider posting some code? I'm not an expert at C. – GeneralPancake Jan 18 '19 at 22:51
  • 1
    It sounds like the problem is that getchar() "blocks". Consider using [curses](https://en.wikipedia.org/wiki/Ncurses). It's made-to-order for text-mode "screen control". Like waiting for the user to hit ... or any other key, such as a control key. It's callable from C, and it's platform-agnostic, including Windows and Linux. – paulsm4 Jan 18 '19 at 22:51

2 Answers2

1
while(getchar() != '\n'){
        continue; 

It does no do what you think.

You want to break first loop.

If(getchar() == char_which_breaks_the_loop) break;
0___________
  • 60,014
  • 4
  • 34
  • 74
1

Here is example code that works using the select call.

It only works for newline because the kernel TTY layer will hold up until it gets a newline.

So, for any other char (e.g. space), we'd have to issue the ioctl calls I mentioned in my top comments to put the layer into "raw" mode (vs. the default of "cooked"). If you need that, see tcgetattr and tcsetattr calls.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <sys/select.h>

int
main(void)
{
    int fd;
    char buf[10];
    int len;
    fd_set rdset;

    fd = 0;

    while (1) {
        // this is do whatever until newline is pressed ...
        printf(".");
        fflush(stdout);

        // set up the select mask -- the list of file descriptors we want to
        // wait on for read/input data available
        FD_ZERO(&rdset);
        FD_SET(fd,&rdset);

        // set timeout of 1ms
        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 1000;

        // wait for action on stdin [or loop if timeout]
        // the first arg must be: the highest fd number in the mask + 1
        len = select(fd + 1,&rdset,NULL,NULL,&tv);
        if (len <= 0)
            continue;

        // we're guaranteed at least one char of input here
        len = read(fd,buf,1);
        if (len <= 0)
            continue;

        // wait for newline -- stop program when we get it
        if (buf[0] == '\n')
            break;
    }

    printf("\n");

    return 0;
}
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • Wow thanks. I really appreciate this. I don't think in a million years I would have figured this out for myself. I know it's probably asking a lot, but would you mind commenting your code? I'd like to understand better what this is doing. Thank you anyway. – GeneralPancake Jan 18 '19 at 23:08
  • You're welcome. I've had to fiddle with this stuff for production code, so I was somewhat already familiar with it. – Craig Estey Jan 18 '19 at 23:20
  • @GeneralPancake: before you wade neck deep into ioctl(), select() and friends - please consider trying [ncurses](https://www.linuxjournal.com/content/getting-started-ncurses), first. – paulsm4 Jan 19 '19 at 19:10