我为Win32控制台下载了Vim源文件(vim74src.zip)并尝试了解Vim Editor如何知道我按下的键的方式?我搜索了一些关键字,例如getch
和ReadConsoleInput
等。但最终,我失败了。
有没有人知道Vim Editor如何知道按下的按键或哪个功能用于获取用户按下的按键信号?
答案 0 :(得分:3)
让我们看看os_win32.c
。我们看到的第一个是:
/*
* os_win32.c
*
* Used for both the console version and the Win32 GUI. A lot of code is for
* the console version only, so there is a lot of "#ifndef FEAT_GUI_W32".
因此,对于仅限控制台的win32代码,我们应该查看此#ifndef
我将在这里发布剥离版本的函数 - 仅用于键盘输入,无需额外处理(鼠标,IME等)
现在我们应该找到一个输入处理函数:
/*
* mch_inchar(): low-level input function.
* Get one or more characters from the keyboard or the mouse.
* If time == 0, do not wait for characters.
* If time == n, wait a short time for characters.
* If time == -1, wait forever for characters.
* Returns the number of characters read into buf.
*/
int
mch_inchar(
char_u *buf,
int maxlen,
long time,
int tb_change_cnt)
{
int len;
int c;
#define TYPEAHEADLEN 20
static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
static int typeaheadlen = 0;
/* First use any typeahead that was kept because "buf" was too small. */
if (typeaheadlen > 0)
goto theend;
if (time >= 0)
{
if (!WaitForChar(time)) /* no character available */
return 0;
}
/*
* Try to read as many characters as there are, until the buffer is full.
*/
/* we will get at least one key. Get more if they are available. */
/* Keep looping until there is something in the typeahead buffer and more
* to get and still room in the buffer (up to two bytes for a char and
* three bytes for a modifier). */
while ((typeaheadlen == 0 || WaitForChar(0L))
&& typeaheadlen + 5 <= TYPEAHEADLEN)
{
char_u ch2 = NUL;
int modifiers = 0;
c = tgetch(&modifiers, &ch2);
/* A key may have one or two bytes. */
typeahead[typeaheadlen] = c;
if (ch2 != NUL)
{
typeahead[typeaheadlen + 1] = ch2;
++n;
}
if (modifiers != 0)
{
/* Prepend modifiers to the character. */
mch_memmove(typeahead + typeaheadlen + 3,
typeahead + typeaheadlen, n);
typeahead[typeaheadlen++] = K_SPECIAL;
typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
typeahead[typeaheadlen++] = modifiers;
}
typeaheadlen += n;
}
theend:
/* Move typeahead to "buf", as much as fits. */
len = 0;
while (len < maxlen && typeaheadlen > 0)
{
buf[len++] = typeahead[0];
mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
}
return len;
}
代码经过充分评论,因此非常明显。这两个重要功能有WaitForChar
和tgetch
。继续调查:
/*
* Wait until console input from keyboard or mouse is available,
* or the time is up.
* Return TRUE if something is available FALSE if not.
*/
static int
WaitForChar(long msec)
{
DWORD dwNow = 0, dwEndTime = 0;
INPUT_RECORD ir;
DWORD cRecords;
char_u ch, ch2;
if (msec > 0)
/* Wait until the specified time has elapsed. */
dwEndTime = GetTickCount() + msec;
else if (msec < 0)
/* Wait forever. */
dwEndTime = INFINITE;
/* We need to loop until the end of the time period, because
* we might get multiple unusable mouse events in that time.
*/
for (;;)
{
if (msec > 0)
{
/* If the specified wait time has passed, return. Beware that
* GetTickCount() may wrap around (overflow). */
dwNow = GetTickCount();
if ((int)(dwNow - dwEndTime) >= 0)
break;
}
if (msec != 0)
{
DWORD dwWaitTime = dwEndTime - dwNow;
if (WaitForSingleObject(g_hConIn, dwWaitTime) != WAIT_OBJECT_0)
continue;
}
cRecords = 0;
PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
if (cRecords > 0)
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, NULL, FALSE))
return TRUE;
else if (msec == 0)
break;
}
return FALSE;
}
GetTickCount
和WaitForSingleObject
在这里有点偏离主题,但您可以在链接上阅读它们。 PeekConsoleInput
从指定的控制台输入缓冲区中读取数据,而不将其从缓冲区中删除。 g_hConIn
是一个控制台输入处理程序,全局变量,其初始化将在后面讨论。使用的下一个功能:
/*
* Get a keystroke or a mouse event
*/
static char_u
tgetch(int *pmodifiers, char_u *pch2)
{
char_u ch;
for (;;)
{
INPUT_RECORD ir;
DWORD cRecords = 0;
if (ReadConsoleInput(g_hConIn, &ir, 1, &cRecords) == 0)
{
if (did_create_conin)
read_error_exit();
create_conin();
continue;
}
if (ir.EventType == KEY_EVENT)
if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2, pmodifiers, TRUE))
return ch;
}
}
ReadConsoleInput
从控制台输入缓冲区中读取数据并将其从缓冲区中删除。decode_key_event
没什么兴趣,它通过将字符和修饰符从WinAPI转换为更有用的格式来填充参数。read_error_exit
是应用程序退出,在ui.c
现在关于g_hConIn
变量和conin
。
这是g_hConIn
在全球范围内定义的方式:
/* Win32 Console handles for input and output */
static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
这就是它的初始化方式:
/*
* non-GUI version of mch_init().
*/
void
mch_init(void)
{
/* Obtain handles for the standard Console I/O devices */
if (read_cmd_fd == 0)
g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
else
create_conin();
}
GetStdHandle
检索指定标准设备的句柄(标准输入,标准输出或标准错误)。STD_INPUT_HANDLE
标准输入设备。最初,这是控制台输入缓冲区CONIN$
。最后一部分:
static int did_create_conin = FALSE;
/*
* Create the console input. Used when reading stdin doesn't work.
*/
static void
create_conin(void)
{
g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, 0, (HANDLE)NULL);
did_create_conin = TRUE;
}
但是,如果CONIN$
已经为我们完成了,我们为什么还需要其他GetStdHandle
处理?关键是stdin
可能会被重定向,而CONIN$
则无法重定向(请参阅What does CreateFile("CONIN$" ..) do?)
答案 1 :(得分:0)
对于vim的windows版本(我猜你也是这样),你可能想看一下os_win32.c文件。看起来函数decode_mouse_event
处理鼠标事件。