我正在创建一个交互式控制台应用程序,应该在Linux和Windows操作系统上运行。在程序中,有提示要求从stdin
进行键盘输入。在每个提示中,用户只能输入特定的ASCII字符,以及特定数量的字符。如果按下<Escape>
或<Enter>
键,提示也有特殊的行为; <Enter>
提交提示以由内部程序状态评估,<Escape>
触发一个命令,该命令退出程序而不需要提交提示。目前,这些提示只在Linux上工作,因为我不知道如何在Windows上实现它们。
输入提示符当前通过两个函数实现:通用处理程序函数prompt_user_input
和平台层函数platform_console_read_key
,后者目前只有一个工作的Linux实现--而不是Windows。platform_console_read_key
是这样工作的:
1.立即从stdin
读取最多6个字节到缓冲区(多个字节允许解析非ASCII键的ANSI转义码)。
1.如果读取的第一个字节 * 不是 * 转义序列指示符,则第一个字节将作为常规ASCII字符返回。
1.如果第一个字节 * 是 * 转义序列指示符,则解析缓冲区的字节2-6。
1.如果缓冲区的其余部分为空,则字符是独立的,执行步骤2。
1.如果缓冲区的后面部分有东西,那么它实际上是一个转义序列。尝试解析并将其作为“扩展键代码”(对于> 255的整数,它只是一个#define
)返回。
1.如果在缓冲区的后面部分有一些东西,并且它是函数没有处理的转义码,则返回特殊值0
。
1.如果有任何类型的严重I/O错误,则返回特殊值KEY_COUNT
。
我这样读取字符的原因是因为我在prompt_user_input
中分别处理platform_console_read_key
返回的每个键代码,以确定程序应该如何进行。prompt_user_input
的工作方式如下:
1.取一个参数,它是可以输入的最大字符数。
1.通过platform_console_read_key
无限循环读取密钥。
1.如果last key匹配<Enter>
或<Escape>
,则中断循环。
1.否则,使用filter_user_input
predicate 查看它是否是有效键。
1.如果它是有效的,将它添加到输入缓冲区,只要写入缓冲区的字符数不超过函数参数所允许的数量。同时将它打印到屏幕上(即“echo”功能)。
1.如果已达到最大写入次数,或者密钥无效,则继续(即,后藤转到步骤2)。
我的问题是如何为Windows平台层实现platform_console_read_key
。我如何在Windows终端立即读取6个字节的键盘输入?另外,我如何确定这6个字节将如何格式化,以便我可以将Windows键代码Map到函数的返回值,从而使函数的行为与Linux版本的行为相匹配?
下面是Linux的工作代码,其中平台层包括<termios.h>
和<unistd.h>
。我省略了一些不相关的代码,并添加了一些printf
语句,以清晰和易于测试。还要注意的是,在prompt_user_input
中,引用了全局( *state ).in
,这是输入缓冲区;假设它对于所提供的char_count
总是足够大。
bool
prompt_user_input
( const u8 char_count
)
{
printf ( "Type your response: " );
// Clear any previous user input.
memset ( ( *state ).in , 0 , sizeof ( ( *state ).in ) );
KEY key;
u8 indx = 0;
for (;;)
{
key = platform_console_read_key ();
if ( key == KEY_COUNT )
{
// ERROR
return false;
}
// Submit response? Y/N
if ( key == KEY_ENTER )
{
return true;
}
// Quit signal? Y/N
if ( key == KEY_ESCAPE )
{
//...
return true;
}
// Handle backspace.
if ( key == KEY_BACKSPACE )
{
if ( !indx )
{
continue;
}
indx -= 1;
( *state ).in[ indx ] = 0;
}
// Response too long? Y/N
else if ( indx >= char_count )
{
indx = char_count;
continue;
}
// Invalid keycode? Y/N
else if ( !filter_user_input ( key ) )
{
continue;
}
// Write to input buffer.
else
{
( *state ).in[ indx ] = key;
indx += 1;
}
// Render the character.
printf ( "%s"
, ( key == KEY_BACKSPACE ) ? "\b \b"
: ( char[] ){ key , 0 }
);
}
}
KEY
platform_console_read_key
( void )
{
KEY key = KEY_COUNT;
// Configure terminal for non-canonical input.
struct termios tty;
struct termios tty_;
tcgetattr ( STDIN_FILENO , &tty );
tty_ = tty;
tty_.c_lflag &= ~( ICANON | ECHO );
tcsetattr ( STDIN_FILENO , TCSANOW , &tty_ );
fflush ( stdout ); // In case echo functionality desired.
// Read the key from the input stream.
char in[ 6 ]; // Reserve up to six bytes to handle special keys.
memset ( in , 0 , sizeof ( in ) );
i32 result = read ( STDIN_FILENO , in , sizeof ( in ) );
// I/O error.
if ( result < 0 )
{
key = KEY_COUNT;
goto platform_console_read_key_end;
}
// End of transmission (I/O error).
if ( in[ 0 ] == 4
|| in[ 1 ] == 4
|| in[ 2 ] == 4
|| in[ 3 ] == 4
|| in[ 4 ] == 4
|| in[ 5 ] == 4
)
{
key = KEY_COUNT;
goto platform_console_read_key_end;
}
// ANSI escape sequence.
if ( *in == '\033' )
{
// Standalone keycode.
if ( !in[ 1 ] )
{
key = KEY_ESCAPE;
goto platform_console_read_key_end;
}
// Composite keycode.
else
{
if ( in[ 1 ] == '[' )
{
// ...
}
else
{
key = 0;
}
goto platform_console_read_key_end;
}
}
// Standalone ASCII character.
else
{
// Backspace key is mapped to ASCII 'delete' (for some reason).
key = ( *in == KEY_DELETE ) ? KEY_BACKSPACE : *in;
goto platform_console_read_key_end;
}
// Reset terminal to canonical input mode.
platform_console_read_key_end:
tcsetattr ( STDIN_FILENO , TCSANOW , &tty );
fflush ( stdout ); // In case echo functionality desired.
return key;
}
字符串
已编辑
解决了,感谢接受的答案。这里是platform_read_console_key
的最小Windows实现,它保持prompt_user_input
的行为相同。它使用<conio.h>
头。
KEY
platform_console_read_key
( void )
{
i32 getch;
getch = _getch ();
// Error.
if ( getch < 0 )
{
return KEY_COUNT;
}
// Standalone ASCII keycode.
if ( getch != 0 && getch != 224 && getch < 0x100 )
{
return newline ( getch ) ? KEY_ENTER
: ( getch == '\033' ) ? KEY_ESCAPE
: getch
;
}
// Extended keycode.
getch = _getch ();
switch ( getch )
{
default: return 0; // Unknown keycode.
}
}
型
1条答案
按热度按时间m528fe3b1#
下面是一个简单的解决方案,用户
Weather Vane
暗示,我已经用了很多年了。运行这段代码还可以让你发现并定义更多的F键--你知道,F1
,F2
,还有arrow keys
,等等。注意事项:你想在
GetKey()
中的扩展键_getch()
上加上256。这将扩展键值放在了像A thru Z
...这样的公共键的范围之外。注意F10
本身只会是值68
--并且 that 与SHIFT + 'D'
的键值匹配。不好,明白吗?此代码也适用于
CTRL
+键组合。也适用于ALT
键组合在限制范围内。ALT
键组合变得有点毛茸茸的,因为ALT+TAB key
,例如,在MS Windows中的应用程序之间切换。因此,当您的应用程序开始与操作系统竞争操作时,而且--根据经验--您可以添加100行代码来补救这些事情,但只能名义上提高性能和范围。代码在Visual Studio中编译,并在发布到SO之前在Win10上测试。
字符串