Search code examples
c++windowssleep

I want to slow down for a while just one command and not the program - beginner level - C ++


I wanted to create a key counter with seconds. I tried this, but it slows down the program, not just the seconds.

#include <iostream>
#include <Windows.h>
#include <conio.h>
using namespace std;

int x = 1;
int secs = 0;
int clicks = 0;


int main() {
    while (true) {
        if (x == 1) {
            secs++;
            Sleep(1000);
            system("CLS");
            cout << "Clicks: " << clicks << "             Time: " << secs;
            x = 2;
        }
        else if (2 == x) {
            _getch();
            clicks++;
            system("CLS");
            cout << "Clicks: " << clicks << "             Time: " << secs;
            x = 1;
        }
    }
}

Solution

  • You seem to want the Sleep function to not block the entire program while waiting for the timer, but to instead continue processing keyboard input while waiting for the timer. Also, you seem to want the getch function to not block the entire program while waiting for keyboard input, but to instead continue processing timers while waiting for keyboard input. However, this is not the way that these functions work. Both functions will continue blocking forever, until the specific type of event occurs that they are waiting for.

    What you instead want is a function that is able to wait on several different types of events simultaneously. The function should block until any one of these events occurs. As soon as such an event occurs, the function should return immediately and specify what type of event occurred, i.e. whether it was a timer event or a keyboard input event. That way, the event can be handled immediately.

    Generally, you can use the function WaitForMultipleObjects for this purpose. However, in this case, the function WaitForSingleObject is sufficient, as that function will allow you to specify a timeout interval in addition to the object handle that you want to wait on. That way, you can wait on both a timer and on an object handle for keyboard input.

    Unfortunately, according to my tests, using WaitForSingleObject on the input handle of a console will only work reliably when processing console input using low-level Console API functions such as ReadConsoleInput. It seems it cannot reliably predict whether kbhit and getch will work, and it also won't work reliably with ReadConsole.

    I believe that this program accomplishes what you want:

    #include <Windows.h>
    #include <iostream>
    #include <cstdio>
    
    int main()
    {
        HANDLE hStdin = INVALID_HANDLE_VALUE;
        ULONGLONG ullStartTime;
    
        //counter of the number of times a keypress was registered
        int num_keypresses = 0;
    
        //get input handle
        hStdin= GetStdHandle( STD_INPUT_HANDLE );
        if ( hStdin == INVALID_HANDLE_VALUE )
        {
            std::cerr << "error opening input handle!\n" << std::flush;
            exit( EXIT_FAILURE );
        }
    
        //remember time that program started
        ullStartTime = GetTickCount64();
    
        for (;;) //infinite loop, equivalent to while(1)
        {
            ULONGLONG ullElapsedTime = GetTickCount64() - ullStartTime;
    
            int seconds   = (int) ( ullElapsedTime / 1000 );
            int remainder = (int) ( ullElapsedTime % 1000 );
    
            //round up to next second, if appropriate
            if ( remainder >= 500 )
            {
                seconds++;
                remainder -= 1000;
            }
    
            //uncomment the folllowing line to clear the screen
            //std::system( "cls" );
    
            //write data to screen
            std::cout << "Keypresses: " << num_keypresses << "     Time: " << seconds << " seconds\n" << std::flush;
    
            //wait for either a keypress to occur, or for a timeout to occur
            switch ( WaitForSingleObject( hStdin, 1000 - remainder ) )
            {
                case WAIT_OBJECT_0:
                {
                    DWORD dwRead;
                    INPUT_RECORD ir;
    
                    //console input event occurred, so read it
                    if ( !ReadConsoleInput( hStdin, &ir, 1, &dwRead ) || dwRead != 1 )
                    {
                        std::cerr << "error reading input!\n" << std::flush;
                        std::exit( EXIT_FAILURE );
                    }
    
                    if ( ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown )
                    {
                        num_keypresses += ir.Event.KeyEvent.wRepeatCount;
                    }
                    break;
                }
                case WAIT_TIMEOUT:
                {
                    //timeout occurred
                    break;
                }
                default:
                {
                    std::cerr << "unexpected error!\n" << std::flush;
                    std::exit( EXIT_FAILURE );
                }
            }
        }
    }
    

    If I wait about 5 seconds before starting to press keys, and then press a few, then the output I get is the following:

    Keypresses: 0     Time: 0 seconds
    Keypresses: 0     Time: 1 seconds
    Keypresses: 0     Time: 2 seconds
    Keypresses: 0     Time: 3 seconds
    Keypresses: 0     Time: 4 seconds
    Keypresses: 0     Time: 5 seconds
    Keypresses: 1     Time: 6 seconds
    Keypresses: 1     Time: 6 seconds
    Keypresses: 2     Time: 7 seconds
    Keypresses: 2     Time: 7 seconds
    Keypresses: 2     Time: 8 seconds
    Keypresses: 3     Time: 9 seconds
    Keypresses: 3     Time: 9 seconds
    Keypresses: 4     Time: 10 seconds
    Keypresses: 4     Time: 10 seconds
    Keypresses: 4     Time: 11 seconds
    Keypresses: 5     Time: 11 seconds
    Keypresses: 5     Time: 11 seconds
    Keypresses: 5     Time: 12 seconds
    Keypresses: 6     Time: 12 seconds
    Keypresses: 6     Time: 13 seconds
    Keypresses: 6     Time: 14 seconds
    Keypresses: 7     Time: 15 seconds
    Keypresses: 7     Time: 15 seconds
    Keypresses: 7     Time: 16 seconds
    Keypresses: 7     Time: 17 seconds
    

    In your original program, you are using std::system("cls"); to clear the screen, instead of printing a new line every time. In my program, I am not clearing the screen, however, you can uncomment the line std::system("cls"); and it should work. I have already put it in the correct place. Note, however, that using std::system is not recommended. See this page from the official Microsoft documentation for better alternatives.