我希望不间断地按住键“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键三秒后,会在屏幕上显示该字符。之后,直到用户释放按键然后再次按下时才会触发新的超时。
我希望不间断地按住键 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键三秒后,会在屏幕上显示该字符。之后,直到用户释放按键然后再次按下时才会触发新的超时。