win,tty: fix read stop in line mode

Closing the handle does not make ReadConsoleW exit reliably on
Windows 7 and above. Thus, after switching from line to raw mode,
keypresses were held until enter was pressed. This makes ReadConsoleW
exit by writing a return keypress to its input buffer, similar to
what was already done for raw mode.

Fixes: https://github.com/libuv/libuv/issues/852
PR-URL: https://github.com/libuv/libuv/pull/866
Reviewed-by: Bert Belder <bertbelder@gmail.com>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
João Reis 2016-05-11 13:03:30 +01:00 committed by Saúl Ibarra Corretgé
parent 4e4407b17e
commit e51442bbc9
6 changed files with 104 additions and 8 deletions

View File

@ -59,7 +59,7 @@ AM_CONDITIONAL([OPENBSD], [AS_CASE([$host_os],[openbsd*], [true], [false])
AM_CONDITIONAL([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])])
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
AS_CASE([$host_os],[mingw*], [
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv"
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -luser32"
])
AC_CHECK_HEADERS([sys/ahafs_evProds.h])
AC_CHECK_PROG(PKG_CONFIG, pkg-config, yes)

View File

@ -83,6 +83,7 @@ extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
#define UV_HANDLE_ZERO_READ 0x00080000
#define UV_HANDLE_EMULATE_IOCP 0x00100000
#define UV_HANDLE_BLOCKING_WRITES 0x00200000
#define UV_HANDLE_CANCELLATION_PENDING 0x00400000
/* Used by uv_tcp_t and uv_udp_t handles */
#define UV_HANDLE_IPV6 0x01000000

View File

@ -871,10 +871,15 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
}
} else {
/* Read successful */
/* TODO: read unicode, convert to utf-8 */
DWORD bytes = req->u.io.overlapped.InternalHigh;
handle->read_cb((uv_stream_t*) handle, bytes, &buf);
if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
/* Read successful */
/* TODO: read unicode, convert to utf-8 */
DWORD bytes = req->u.io.overlapped.InternalHigh;
handle->read_cb((uv_stream_t*) handle, bytes, &buf);
} else {
handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
handle->read_cb((uv_stream_t*) handle, 0, &buf);
}
}
/* Wait for more input events. */
@ -937,6 +942,9 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
int uv_tty_read_stop(uv_tty_t* handle) {
INPUT_RECORD record;
DWORD written;
handle->flags &= ~UV_HANDLE_READING;
DECREASE_ACTIVE_COUNT(handle->loop, handle);
@ -944,8 +952,6 @@ int uv_tty_read_stop(uv_tty_t* handle) {
if ((handle->flags & UV_HANDLE_READ_PENDING) &&
(handle->flags & UV_HANDLE_TTY_RAW)) {
/* Write some bullshit event to force the console wait to return. */
INPUT_RECORD record;
DWORD written;
memset(&record, 0, sizeof record);
if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
return GetLastError();
@ -954,7 +960,20 @@ int uv_tty_read_stop(uv_tty_t* handle) {
/* Cancel line-buffered read */
if (handle->tty.rd.read_line_handle != NULL) {
/* Closing this handle will cancel the ReadConsole operation */
if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
/* Write enter key event to force the console wait to return. */
record.EventType = KEY_EVENT;
record.Event.KeyEvent.bKeyDown = TRUE;
record.Event.KeyEvent.wRepeatCount = 1;
record.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
record.Event.KeyEvent.wVirtualScanCode =
MapVirtualKeyW(VK_RETURN, MAPVK_VK_TO_VSC);
record.Event.KeyEvent.uChar.UnicodeChar = L'\r';
record.Event.KeyEvent.dwControlKeyState = 0;
WriteConsoleInputW(handle->handle, &record, 1, &written);
handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
}
/* Close line-buffered read handle */
CloseHandle(handle->tty.rd.read_line_handle);
handle->tty.rd.read_line_handle = NULL;
}

View File

@ -43,6 +43,9 @@ TEST_DECLARE (semaphore_1)
TEST_DECLARE (semaphore_2)
TEST_DECLARE (semaphore_3)
TEST_DECLARE (tty)
#ifdef _WIN32
TEST_DECLARE (tty_raw)
#endif
TEST_DECLARE (tty_file)
TEST_DECLARE (tty_pty)
TEST_DECLARE (stdio_over_pipes)
@ -387,6 +390,9 @@ TASK_LIST_START
#endif
TEST_ENTRY (pipe_set_non_blocking)
TEST_ENTRY (tty)
#ifdef _WIN32
TEST_ENTRY (tty_raw)
#endif
TEST_ENTRY (tty_file)
TEST_ENTRY (tty_pty)
TEST_ENTRY (stdio_over_pipes)

View File

@ -146,6 +146,75 @@ TEST_IMPL(tty) {
}
#ifdef _WIN32
static void tty_raw_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
buf->base = malloc(size);
buf->len = size;
}
static void tty_raw_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) {
if (nread > 0) {
ASSERT(nread == 1);
ASSERT(buf->base[0] == ' ');
uv_close((uv_handle_t*) tty_in, NULL);
} else {
ASSERT(nread == 0);
}
}
TEST_IMPL(tty_raw) {
int r;
int ttyin_fd;
uv_tty_t tty_in;
uv_loop_t* loop = uv_default_loop();
HANDLE handle;
INPUT_RECORD record;
DWORD written;
/* Make sure we have an FD that refers to a tty */
handle = CreateFileA("conin$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
ASSERT(handle != INVALID_HANDLE_VALUE);
ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
ASSERT(ttyin_fd >= 0);
ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
ASSERT(r == 0);
r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read);
ASSERT(r == 0);
/* Give uv_tty_line_read_thread time to block on ReadConsoleW */
Sleep(100);
/* Turn on raw mode. */
r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
ASSERT(r == 0);
/* Write ' ' that should be read in raw mode */
record.EventType = KEY_EVENT;
record.Event.KeyEvent.bKeyDown = TRUE;
record.Event.KeyEvent.wRepeatCount = 1;
record.Event.KeyEvent.wVirtualKeyCode = VK_SPACE;
record.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(VK_SPACE, MAPVK_VK_TO_VSC);
record.Event.KeyEvent.uChar.UnicodeChar = L' ';
record.Event.KeyEvent.dwControlKeyState = 0;
WriteConsoleInputW(handle, &record, 1, &written);
uv_run(loop, UV_RUN_DEFAULT);
MAKE_VALGRIND_HAPPY();
return 0;
}
#endif
TEST_IMPL(tty_file) {
#ifndef _WIN32
uv_loop_t loop;

1
uv.gyp
View File

@ -112,6 +112,7 @@
'-liphlpapi',
'-lpsapi',
'-lshell32',
'-luser32',
'-luserenv',
'-lws2_32'
],