Vim Editor(在控制台子系统下)如何知道我按下的键?

时间:2014-01-16 13:31:21

标签: vim

我为Win32控制台下载了Vim源文件(vim74src.zip)并尝试了解Vim Editor如何知道我按下的键的方式?我搜索了一些关键字,例如getchReadConsoleInput等。但最终,我失败了。

有没有人知道Vim Editor如何知道按下的按键或哪个功能用于获取用户按下的按键信号?

2 个答案:

答案 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;

}

代码经过充分评论,因此非常明显。这两个重要功能有WaitForChartgetch。继续调查:

/*
 * 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;
}
  • GetTickCountWaitForSingleObject在这里有点偏离主题,但您可以在链接上阅读它们。
  • 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处理鼠标事件。