1

Possible Duplicate:
C/C++: Capture characters from standard input without waiting for enter to be pressed

char ch;

think I want to get a character. I know two different ways.

1- using cin command in iostream header file.

cin >> ch;

It will wait for user to type something, then user MUST press enter to send input into ch variable.

2- getch() command in conio.h header file.

ch = _getch();

It will wait for user to type something, as soon as typing a character it will be saved in ch variable and user DOES NOT need to press enter. BTW this code will stop program and will wait for an input.

Now I want to write a command which does not need pressing enter and it does not stop program for pressing something. Just imagine I delay program for 1 seconds, if user presses something it will save it into ch variable, if not nothing, program will continue and it won't stop for pressing something. It is like a picker command, if there is something it will pick it up, if not it will continue.

Hope I'm clear. So how to do it?

Community
  • 1
  • 1
Inside Man
  • 4,194
  • 12
  • 59
  • 119
  • 1
    You posted a similar question some time ago. Did you check out the link I suggested? – Luchian Grigore May 27 '12 at 18:04
  • 1
    So you want to wait for a certain amount of time or continue earlier if the user presses a key? – Trevor Hickey May 27 '12 at 18:06
  • 1
    The answer to this is OS-specific, and you haven't mentioned the OS. – Ben Voigt May 27 '12 at 18:27
  • 1
    The proposed duplicate is NOT at all a duplicate. The other question asks for the first key pressed to be reported, without waiting for enter. This question wants to continue even if no key is pressed at all. Very different. – Ben Voigt May 27 '12 at 18:29
  • 2
    @BenVoigt, judging by the `_getch()`, I presume Windows. I don't know of any other `_getch()`. – chris May 27 '12 at 18:30
  • 2
    @chris: I agree, but I want Stranger to go ahead and fill in the details, instead of answering based on a presumption. – Ben Voigt May 27 '12 at 18:31
  • 1
    @Stranger, if you are using Windows, you can use `GetKeyboardState` or a hook. – chris May 27 '12 at 18:31

2 Answers2

2

On Windows, you can do this:

  1. CreateFile using the filename "CONIN$", which will give you a Win32 handle to the console.
  2. WaitForSingleObject, passing the console handle and a timeout.
  3. If the wait succeeds, use ReadConsoleInput to determine what even happened.
  4. If the wait fails, the timeout occurred.

If you're just polling in a loop performing some other action, then you can use PeekConsoleInput, which checks to see if any events are in the input queue, and always returns immediately.


On Unix, a similar approach will work. Just note that:

  1. It's not necessary to open a file, since stdin is always file descriptor 0.
  2. Use select or poll to test for activity on the input with a timeout.

Final difference: On Windows, mouse activity is captured the same way. On Unix, you'll usually be reading keyboard input from a tty, and mouse stuff is totally separate.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
1

First we need a function to turn on and off nonblocking input:

void nonblock(const bool state){

    struct termios ttystate;

    //get the terminal state
    tcgetattr(STDIN_FILENO, &ttystate);

    if (state){
        //turn off canonical mode
        ttystate.c_lflag &= ~ICANON;
        //minimum of number input read.
        ttystate.c_cc[VMIN] = 1;
    }
    else{
        //turn on canonical mode
        ttystate.c_lflag |= ICANON;
    }

    //set the terminal attributes.
    tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}

Now we need a function to test and see if a key was pressed:

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

We are going to be checking for two things in parallel. Has the user pressed a key, or has time run out? Here is a function to change a boolean value after a specified number of seconds:

void SleepForNumberOfSeconds(const int & numberofSeconds,bool & timesUp){

    timespec delay = {numberofSeconds,0};
    timespec delayrem;

    nanosleep(&delay, &delayrem);
    timesUp = true;

    return;
}

Here is the main function you will be able to call:

void WaitForTimeoutOrInterrupt(int const& numberofSeconds){

    bool timesUp = false;

    std::thread t(SleepForNumberOfSeconds, numberofSeconds, std::ref(timesUp));
    nonblock(1);
    while (!timesUp && !keypress()){

    }

    if (t.joinable()){
        t.detach();
    }
    nonblock(0);

    return;
}

Here is the code to test out.
compiled with:

g++ -std=c++0x -o rand rand.cpp -lpthread

on:

gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1

This is just one solution, and it may not work for you.
Consider looking into ncurses as well.

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271