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:
parent
4e4407b17e
commit
e51442bbc9
@ -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([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])])
|
||||||
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
|
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
|
||||||
AS_CASE([$host_os],[mingw*], [
|
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_HEADERS([sys/ahafs_evProds.h])
|
||||||
AC_CHECK_PROG(PKG_CONFIG, pkg-config, yes)
|
AC_CHECK_PROG(PKG_CONFIG, pkg-config, yes)
|
||||||
|
|||||||
@ -83,6 +83,7 @@ extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
|
|||||||
#define UV_HANDLE_ZERO_READ 0x00080000
|
#define UV_HANDLE_ZERO_READ 0x00080000
|
||||||
#define UV_HANDLE_EMULATE_IOCP 0x00100000
|
#define UV_HANDLE_EMULATE_IOCP 0x00100000
|
||||||
#define UV_HANDLE_BLOCKING_WRITES 0x00200000
|
#define UV_HANDLE_BLOCKING_WRITES 0x00200000
|
||||||
|
#define UV_HANDLE_CANCELLATION_PENDING 0x00400000
|
||||||
|
|
||||||
/* Used by uv_tcp_t and uv_udp_t handles */
|
/* Used by uv_tcp_t and uv_udp_t handles */
|
||||||
#define UV_HANDLE_IPV6 0x01000000
|
#define UV_HANDLE_IPV6 0x01000000
|
||||||
|
|||||||
@ -871,10 +871,15 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Read successful */
|
if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
|
||||||
/* TODO: read unicode, convert to utf-8 */
|
/* Read successful */
|
||||||
DWORD bytes = req->u.io.overlapped.InternalHigh;
|
/* TODO: read unicode, convert to utf-8 */
|
||||||
handle->read_cb((uv_stream_t*) handle, bytes, &buf);
|
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. */
|
/* 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) {
|
int uv_tty_read_stop(uv_tty_t* handle) {
|
||||||
|
INPUT_RECORD record;
|
||||||
|
DWORD written;
|
||||||
|
|
||||||
handle->flags &= ~UV_HANDLE_READING;
|
handle->flags &= ~UV_HANDLE_READING;
|
||||||
DECREASE_ACTIVE_COUNT(handle->loop, handle);
|
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) &&
|
if ((handle->flags & UV_HANDLE_READ_PENDING) &&
|
||||||
(handle->flags & UV_HANDLE_TTY_RAW)) {
|
(handle->flags & UV_HANDLE_TTY_RAW)) {
|
||||||
/* Write some bullshit event to force the console wait to return. */
|
/* Write some bullshit event to force the console wait to return. */
|
||||||
INPUT_RECORD record;
|
|
||||||
DWORD written;
|
|
||||||
memset(&record, 0, sizeof record);
|
memset(&record, 0, sizeof record);
|
||||||
if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
|
if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
|
||||||
return GetLastError();
|
return GetLastError();
|
||||||
@ -954,7 +960,20 @@ int uv_tty_read_stop(uv_tty_t* handle) {
|
|||||||
|
|
||||||
/* Cancel line-buffered read */
|
/* Cancel line-buffered read */
|
||||||
if (handle->tty.rd.read_line_handle != NULL) {
|
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);
|
CloseHandle(handle->tty.rd.read_line_handle);
|
||||||
handle->tty.rd.read_line_handle = NULL;
|
handle->tty.rd.read_line_handle = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,9 @@ TEST_DECLARE (semaphore_1)
|
|||||||
TEST_DECLARE (semaphore_2)
|
TEST_DECLARE (semaphore_2)
|
||||||
TEST_DECLARE (semaphore_3)
|
TEST_DECLARE (semaphore_3)
|
||||||
TEST_DECLARE (tty)
|
TEST_DECLARE (tty)
|
||||||
|
#ifdef _WIN32
|
||||||
|
TEST_DECLARE (tty_raw)
|
||||||
|
#endif
|
||||||
TEST_DECLARE (tty_file)
|
TEST_DECLARE (tty_file)
|
||||||
TEST_DECLARE (tty_pty)
|
TEST_DECLARE (tty_pty)
|
||||||
TEST_DECLARE (stdio_over_pipes)
|
TEST_DECLARE (stdio_over_pipes)
|
||||||
@ -387,6 +390,9 @@ TASK_LIST_START
|
|||||||
#endif
|
#endif
|
||||||
TEST_ENTRY (pipe_set_non_blocking)
|
TEST_ENTRY (pipe_set_non_blocking)
|
||||||
TEST_ENTRY (tty)
|
TEST_ENTRY (tty)
|
||||||
|
#ifdef _WIN32
|
||||||
|
TEST_ENTRY (tty_raw)
|
||||||
|
#endif
|
||||||
TEST_ENTRY (tty_file)
|
TEST_ENTRY (tty_file)
|
||||||
TEST_ENTRY (tty_pty)
|
TEST_ENTRY (tty_pty)
|
||||||
TEST_ENTRY (stdio_over_pipes)
|
TEST_ENTRY (stdio_over_pipes)
|
||||||
|
|||||||
@ -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) {
|
TEST_IMPL(tty_file) {
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
uv_loop_t loop;
|
uv_loop_t loop;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user