gpt4 book ai didi

c - Windows Shell 中的 ReadConsoleInput 缺少事件?

转载 作者:行者123 更新时间:2023-12-05 07:57:13 24 4
gpt4 key购买 nike

当我继续(可能是徒劳的)尝试重新实现一个支持 *nix 和 windows 的 curses 样式库时,我偶然发现了一个问题,即使用 windows api 读取终端导入。

基本上,我没有得到我期望的所有事件,我也不知道为什么。

首先我将终端设置为非缓冲模式:

DWORD mode;
HANDLE hstdin = GetStdHandle( STD_INPUT_HANDLE );

// Save old mode
GetConsoleMode(hstdin, &mode);

// Set to no line-buffering, no echo, no special-key-processing
SetConsoleMode(hstdin, 0);

然后我在循环中使用 PeekConsoleInput 和 ReadConsoleInput 来获得非阻塞按键输入;相当于在 linux 中使用 termios.h 和 select on stdin:

__EXPORT int sterm_read(void *state) {
DWORD dwRead;
INPUT_RECORD inRecords[1];
PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 1, &dwRead);
if (dwRead > 0) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 1, &dwRead);
if (inRecords[0].EventType == KEY_EVENT) {
if (inRecords[0].Event.KeyEvent.bKeyDown) {
return inRecords[0].Event.KeyEvent.wVirtualKeyCode;
}
}
}
return -1;
}

忽略状态变量;这样 api 就可以在各种平台上接受任意状态结构。

现在如果我尝试使用这段代码:

#include <sterm.h>
#include <stdio.h>
#define assert(v, msg) if (!v) { printf("FAILED! %s", msg); return 1; }
int main(void) {
void *state = sterm_init();
int i;
char c;
for (;;) {
if ((c = sterm_read(state)) == 81) { // ie. press q to exit
break;
}
if (c != -1) {
sterm_write(state, &c, 1); // This is a thin wrapper around _write(1, ...)
}
}
sterm_shutdown(state);
return 0;
}

它几乎可以工作。我将我按下的输入字符推送到终端......主要是。

可能每按下 10 个字符就会被记录下来。如果我快速输入,API 会“丢失”事件,我会得到“HEO WLD”而不是“HELLO WORLD”。

这是怎么回事? ReadConsoleInput 是否以某种方式清除了输入缓冲区?

我做错了什么吗?似乎我只是根据竞争条件获取事件,即“调用 PeekConsoleInput 时按下了键”。

...但肯定不应该是这样吗?使用这些缓冲 I/O 接口(interface)(而不是 GetAsyncKeyState)的要点是事件应该正确缓冲吗?

帮助!

最佳答案

我还发现不能保证事件会一直存在以供阅读。这是有道理的,否则操作系统将需要提供大量缓冲空间。

我能做的最好的事情就是用这段代码来做我自己的缓冲但显然超过 128 个字符的粘贴通常会失败:

static int g_eaten_ct = 0;       /* Re-eaten char */
static int g_eaten_ix = -1;
static int g_eaten[128];


void reeat(int c)

{ g_eaten_ct += 1;
g_eaten[g_eaten_ix + g_eaten_ct] = c; /* save the char for later */
}


void flush_typah()

{
g_eaten_ct = 0;
g_eaten_ix = -1;

while (_kbhit())
(void)ttgetc();
}


int ttgetc()

{ if (g_eaten_ct > 0)
{ g_eaten_ct -= 1;
return g_eaten[++g_eaten_ix];
}
{ int totalwait = g_timeout_secs;
int oix = -1;

while (1)
{ int got,need;
const DWORD lim = 1000;
INPUT_RECORD rec[32];

int cc = WaitForSingleObject(g_ConsIn, lim);
switch(cc)
{ case WAIT_OBJECT_0:
need = sizeof(g_eaten)/sizeof(g_eaten[0]) - oix;
if (need > 32)
need = 32;
cc = ReadConsoleInput(g_ConsIn,&rec[0],need,(DWORD*)&got);
if (cc && got > 0)
break;
#if _DEBUG
{ DWORD errn = GetLastError();
if (errn != 6)
mlwrite("%pError %d %d ", cc, errn);
}
#endif
continue;
case WAIT_TIMEOUT:
#if _DEBUG
if (g_got_ctrl)
{ g_got_ctrl = false;
return (int)(CTRL | 'C');
}
#endif
if (--totalwait == 0) // -w opt
exit(2);
// drop through
default:continue;
}

{ int ix = -1;
while (++ix < got)
{ INPUT_RECORD * r = &rec[ix];
if (r->EventType == KEY_EVENT && r->Event.KeyEvent.bKeyDown)
{ int ctrl = 0;
int keystate = r->Event.KeyEvent.dwControlKeyState;
if (keystate & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
{ ctrl |= CTRL;
g_chars_since_ctrl = 0;
}

{ int chr = r->Event.KeyEvent.wVirtualKeyCode;
if (in_range(chr, 0x10, 0x12))
continue; /* shifting key only */

if (keystate & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
ctrl |= ALTD;
else
chr = r->Event.KeyEvent.uChar.AsciiChar & 0xff;

if (/*chr != 0x7c && */ (chr | 0x60) != 0x7c) // | BSL < or ^ BSL
{ int vsc = r->Event.KeyEvent.wVirtualScanCode;
if (in_range(vsc, SCANK_STT, 0x58))
{ ctrl |= SPEC;
chr = scantokey[vsc - SCANK_STT];
}
// else if (in_range(vsc, 2, 10) && chr == 0)
// chr = '0' - 1 + vsc;
}

if ((keystate & SHIFT_PRESSED) && ctrl) // exclude e.g. SHIFT 7
ctrl |= SHFT;

g_eaten[++oix] = ctrl | (chr == 0xdd ? 0x7c : chr);
++g_chars_since_ctrl;
}}
else if (r->EventType == MENU_EVENT)
{ /*loglog1("Menu %x", r->Event.MenuEvent.dwCommandId);*/
}
}

if (got == need && oix < sizeof(g_eaten) / sizeof(int))
{ PeekConsoleInput(g_ConsIn, &rec[0], 1, (DWORD*)&got);
if (got > 0)
continue;
}

if (oix >= 0)
{ g_eaten_ct = oix;
g_eaten_ix = 0;
return g_eaten[0];
}
}}
}}

关于c - Windows Shell 中的 ReadConsoleInput 缺少事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27586641/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com