Lets take a look at os_win32.c
. The first that we see is:
/*
* 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".
So for console-only win32 code we should look under this #ifndef
.
I'll post here stripped versions of functions - for keyboard input only, without extra handling (mouse, IME, etc).
Now we should find a function for input handling:
/*
* 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;
}
The code is throughly commented so it is pretty self-explanatory. The two important functions there are WaitForChar
and tgetch
. Continuing investigate:
/*
* 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
and WaitForSingleObject
are kind of off-topic here but you can read about them on the links.
PeekConsoleInput
reads data from the specified console input buffer without removing it from the buffer.
g_hConIn
is a console input handler, global variable, its initialization will be discussed later.
The next function that was used:
/*
* 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
reads data from a console input buffer and removes it from the buffer.
decode_key_event
is not much of an interest, it fills arguments by converting chars and modifiers from WinAPI to more usable format.
read_error_exit
is application exit, defined in ui.c
Now about g_hConIn
variable and conin
.
This is how g_hConIn
defined in global scope:
/* Win32 Console handles for input and output */
static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
This is how it's initialized:
/*
* 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
retrieves a handle to the specified standard device (standard input, standard output, or standard error).
STD_INPUT_HANDLE
The standard input device. Initially, this is the console input buffer, CONIN$
.
And the last part:
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;
}
But why do we need other CONIN$
handling if it is already done for us by GetStdHandle
? The point is that stdin
may be redirected whereas CONIN$
can't (See What does CreateFile("CONIN$" ..) do?)