Prevent entry at specific times

I have a text game in c that uses scanf.

There are several times when a player must enter things, however, while he is not there, the cursor remains in the game, allowing the user to enter whatever he wants, which destroys future scanfs and history.

Is there a way to block input if there is no scan waiting for a response?

+4
source share
2 answers

I think it would be useful to step back and think about all the moving parts that exist in the runtime of your program.

, . (, - , ssh). .

( ), . , , , , , , .

, , . API- C, , termios.

, : TTY demystified. TTY. termios , , stty, termios.

( , , , Linux, Unix- , Mac OS X.)


, "" , , .

, , - ECHO ICANON. .

ECHO, , , . , , , .

ICANON, , , . read(). , , , .

:

1: , ECHO ICANON.

2: , .

3: , , ECHO ICANON.

4: .

5: 1. .

3 , . , stdin read() , . , , , -. .

, . , termios fcntl() ( O_NONBLOCK, ), , read() errno, EAGAIN, . - poll() select(), , ; , read().

, select(), :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

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

struct termios g_terminalSettings; // global to track and change terminal settings

void disableInput(void);
void enableInput(void);

void discardInputBuffer(void);
void discardInputLine(void);

void setTermiosBit(int fd, tcflag_t bit, int onElseOff );
void turnEchoOff(void);
void turnEchoOn(void);
void turnCanonOff(void);
void turnCanonOn(void);

int main(void) {

    // prevent input immediately
    disableInput();

    printf("welcome to the game\n");

    // infinite game loop
    int line = 1;
    int quit = 0;
    while (1) {

        // print dialogue
        for (int i = 0; i < 3; ++i) {
            printf("line of dialogue %d\n",line++);
            sleep(1);
        } // end for

        // input loop
        enableInput();
        int input;
        while (1) {
            printf("choose a number in 1:3 (-1 to quit)\n");
            int ret = scanf("%d",&input);
            discardInputLine(); // clear any trailing garbage (can do this immediately for all cases)
            if (ret == EOF) {
                if (ferror(stdin)) { fprintf(stderr, "[error] scanf() failed: %s", strerror(errno) ); exit(1); }
                printf("end of input\n");
                quit = 1;
                break;
            } else if (ret == 0) { // invalid syntax
                printf("invalid input\n");
            } else if (input == -1) { // quit code
                quit = 1;
                break;
            } else if (!(input >= 1 && input <= 3)) { // invalid value
                printf("number is out-of-range\n");
            } else { // valid
                printf("you entered %d\n",input);
                break;
            } // end if
        } // end while
        if (quit) break;
        disableInput();

    } // end while

    printf("goodbye\n");

    return 0;

} // end main()

void disableInput(void) {
    turnEchoOff(); // so the terminal won't display all the crap the user decides to type during gameplay
    turnCanonOff(); // so the terminal will return crap characters immediately, so we can clear them later without waiting for a LF
} // end disableInput()

void enableInput(void) {
    discardInputBuffer(); // clear all crap characters before enabling input
    turnCanonOn(); // so the user can type and edit a full line of input before submitting it
    turnEchoOn(); // so the user can see what he doing as he typing
} // end enableInput()

void turnEchoOff(void) { setTermiosBit(0,ECHO,0); }
void turnEchoOn(void) { setTermiosBit(0,ECHO,1); }

void turnCanonOff(void) { setTermiosBit(0,ICANON,0); }
void turnCanonOn(void) { setTermiosBit(0,ICANON,1); }

void setTermiosBit(int fd, tcflag_t bit, int onElseOff ) {
    static int first = 1;
    if (first) {
        first = 0;
        tcgetattr(fd,&g_terminalSettings);
    } // end if
    if (onElseOff)
        g_terminalSettings.c_lflag |= bit;
    else
        g_terminalSettings.c_lflag &= ~bit;
    tcsetattr(fd,TCSANOW,&g_terminalSettings);
} // end setTermiosBit()

void discardInputBuffer(void) {
    struct timeval tv;
    fd_set rfds;
    while (1) {
        // poll stdin to see if there anything on it
        FD_ZERO(&rfds);
        FD_SET(0,&rfds);
        tv.tv_sec = 0;
        tv.tv_usec = 0;
        if (select(1,&rfds,0,0,&tv) == -1) { fprintf(stderr, "[error] select() failed: %s", strerror(errno) ); exit(1); }
        if (!FD_ISSET(0,&rfds)) break; // can break if the input buffer is clean
        // select() doesn't tell us how many characters are ready to be read; just grab a big chunk of whatever is there
        char buf[500];
        ssize_t numRead = read(0,buf,500);
        if (numRead == -1) { fprintf(stderr, "[error] read() failed: %s", strerror(errno) ); exit(1); }
        printf("[debug] cleared %d chars\n",numRead);
    } // end while
} // end discardInputBuffer()

void discardInputLine(void) {
    // assumes the input line has already been submitted and is sitting in the input buffer
    int c;
    while ((c = getchar()) != EOF && c != '\n');
} // end discardInputLine()

, discardInputLine() , discardInputBuffer() enableInput(). , , scanf(), . , . , , , , .

, :

welcome to the game
line of dialogue 1
line of dialogue 2
line of dialogue 3
[debug] cleared 12 chars
choose a number in 1:3 (-1 to quit)
0
number is out-of-range
choose a number in 1:3 (-1 to quit)
4
number is out-of-range
choose a number in 1:3 (-1 to quit)
asdf
invalid input
choose a number in 1:3 (-1 to quit)
asdf 1 2 3
invalid input
choose a number in 1:3 (-1 to quit)
0 1
number is out-of-range
choose a number in 1:3 (-1 to quit)
1 4
you entered 1
line of dialogue 4
line of dialogue 5
line of dialogue 6
choose a number in 1:3 (-1 to quit)
2
you entered 2
line of dialogue 7
line of dialogue 8
line of dialogue 9
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 238 chars
choose a number in 1:3 (-1 to quit)
-1
goodbye

12 , . . , . ( , ), , , select()/read().

+8

Linux HP-UX

,

stty -echo 

,

stty echo  
+1

Source: https://habr.com/ru/post/1651149/


All Articles