我希望不间断地按住键“A”至少三秒钟。确保是这样

I want key `A` to be held for at least three seconds without a break. Make sure that is the case

我希望不间断地按住键 A 至少三秒钟。如果按键被打断,三秒钟的时间应该再次适用。 这是我以前的节目。不幸的是,虽然 timer() 过程是 运行,但程序不会注意到任何有关释放或再次按下按钮的信息。

我的问题是:我能做什么?

我想留在C.

我的 IDE 是 Microsoft Visual Studio Community 2019,版本 16.11.2。

我和 Windows 10.

一起工作
#pragma warning(disable : 4996) // Visual Studio doesn't want to see old functions like ‘scanf’.

#include <stdio.h>
#include <Windows.h>
#include <cstdint>
#include <time.h>

bool supposed_to_run = true;
VOID KeyEventProc(KEY_EVENT_RECORD);
long long waited = 0ll;
time_t Time_of_the_keydown_event;
bool triggered = false;


void timer()
{
    Time_of_the_keydown_event;
    time_t _to = time(&_to) + 3ll;

    do
    {
        Time_of_the_keydown_event = time(&Time_of_the_keydown_event);
    } while (Time_of_the_keydown_event < _to && triggered);

}

void Logic(INPUT_RECORD rec)
{
    if (rec.Event.KeyEvent.wVirtualKeyCode == 0)
    {
        return;
    }

    if (!rec.Event.KeyEvent.bKeyDown)
    {
        triggered = false;
        return;
    }

    switch (rec.Event.KeyEvent.wVirtualKeyCode)
    {
    case VK_SHIFT:
        break;
    case VK_CONTROL:
        break;
    case VK_SPACE:
        break;
    case VK_LEFT:
        break;
    case VK_UP:
        break;
    case VK_RIGHT:
        break;
    case VK_DOWN:
        break;
    case 0x41: /*A*/
        triggered = true;
        timer();
        
        printf("A\n");
        break;
    case 0x51: /*Q*/
        break;
    case 0x5A:  /*Z*/
        supposed_to_run = false;
        break;
    default:
        break;
    }
}

int main(void)
{
    INPUT_RECORD rec{};
    HANDLE hConInp = GetStdHandle(STD_INPUT_HANDLE);
    DWORD ReadCount = 0;

    //Change_size_position_and_color();

    while (supposed_to_run)
    {
        ReadConsoleInput(hConInp, &rec, 1, &ReadCount);
        Logic(rec); 
    }
    

    // end program
    printf("\nProgramm wird beendet. Dr\x81 \bcken Sie eine Taste und \x27 \bEnter\x27 \b.\n");
    getchar();
    return 0;
}

我推荐你使用函数WaitForSingleObject。该函数允许您等待新的控制台输入可用,还允许您指定超时,在您的情况下应该是 3 秒。

根据该函数的 return 值,您将知道是否发生了新的控制台输入或是否超时。

switch ( WaitForSingleObject( hConInp, 3000 ) )
{
case WAIT_OBJECT_0:
    //handle new console input
    //ReadConsoleInput will not block if called now
    break;
case WAIT_TIMEOUT:
    //handle timeout
    break;
default:
    //handle error
}

但是,这个解决方案有一个问题。按住键的同时,键会自动重复,产生新的输入事件。因此,3 秒的超时可能永远不会过期(除非您将操作系统配置为具有很长的重复延迟)。

因此,您将不得不忽略这些按键按下事件并再次调用 WaitForSingleObject,但这次不会再次超时 3 秒。相反,新的超时应该是原来 3 秒的剩余时间。

在您的代码中,您使用的是 ISO C 函数 time. However, this function has a precision of only one second, which may not be accurate enough. Therefore, you may want to use the platform-specific function GetTickCount

我相信通过进行上述更改,我能够使您的程序正常运行。这是我的代码:

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

bool supposed_to_run = true;
bool triggered = false;
bool require_release = false;
DWORD remaining_timeout;

void Logic( INPUT_RECORD *p_rec )
{
    //only handle keyboard events
    if ( p_rec->EventType != KEY_EVENT )
        return;

    if (!p_rec->Event.KeyEvent.bKeyDown)
    {
        if ( triggered )
        {
            printf( "removing trigger\n" );
            triggered = false;
        }
        else if ( require_release && p_rec->Event.KeyEvent.wVirtualKeyCode == 0x41 )
        {
            printf( "key has been released and timeout may now be triggered again\n" );
            require_release = false;
        }
        return;
    }

    switch (p_rec->Event.KeyEvent.wVirtualKeyCode)
    {
    case VK_SHIFT:
        break;
    case VK_CONTROL:
        break;
    case VK_SPACE:
        break;
    case VK_LEFT:
        break;
    case VK_UP:
        break;
    case VK_RIGHT:
        break;
    case VK_DOWN:
        break;
    case 0x41: /*A*/
        if ( !triggered && !require_release )
        {
            printf( "triggering timeout\n" );
            triggered = true;
            remaining_timeout = 3000;
        }
        break;
    case 0x51: /*Q*/
        break;
    case 0x5A:  /*Z*/
        supposed_to_run = false;
        break;
    default:
        break;
    }
}

int main(void)
{
    INPUT_RECORD rec;
    HANDLE hConInp = GetStdHandle(STD_INPUT_HANDLE);
    DWORD ReadCount;

    while (supposed_to_run)
    {
        if ( triggered )
        {
            bool timeout_expired = false;

            DWORD start_time = GetTickCount();

            switch ( WaitForSingleObject( hConInp, remaining_timeout ) )
            {
            case WAIT_OBJECT_0:
                break;
            case WAIT_TIMEOUT:
                timeout_expired = true;
                break;
            default:
                fprintf( stderr, "unexpected error!" );
                exit( EXIT_FAILURE );
            }

            DWORD time_expired = GetTickCount() - start_time;

            if ( !timeout_expired )
            {
                //It is possible that WaitForSingleObject did not report
                //a timeout, but that GetTickCount now reports that the
                //timeout expired. We should consider this also a timeout.
                if ( time_expired >= remaining_timeout )
                    timeout_expired = true;
            }

            if ( timeout_expired )
            {
                triggered = false;
                require_release = true;
                printf( "A\n" );
                continue;
            }
            else
            {
                remaining_timeout -= time_expired;
            }
        }

        ReadConsoleInput( hConInp, &rec, 1, &ReadCount );
        Logic( &rec ); 
    }

    return 0;
}

用户按住A键三秒后,会在屏幕上显示该字符。之后,直到用户释放按键然后再次按下时才会触发新的超时。