All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
To: dev@dpdk.org
Cc: Dmitry Malloy <dmitrym@microsoft.com>,
	Narcisa Ana Maria Vasile <Narcisa.Vasile@microsoft.com>,
	Fady Bader <fady@mellanox.com>,
	Tal Shnaiderman <talshn@mellanox.com>,
	"Kadam, Pallavi" <pallavi.kadam@intel.com>,
	Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>,
	Thomas Monjalon <thomas@monjalon.net>,
	Olivier Matz <olivier.matz@6wind.com>
Subject: [dpdk-dev] [PATCH v2 6/7] cmdline: support Windows
Date: Fri, 31 Jul 2020 00:06:50 +0300	[thread overview]
Message-ID: <20200730210652.14568-7-dmitry.kozliuk@gmail.com> (raw)
In-Reply-To: <20200730210652.14568-1-dmitry.kozliuk@gmail.com>

Implement terminal handling, input polling, and vdprintf() for Windows.

Because Windows I/O model differs fundamentally from Unix and there is
no concept of character device, polling is simulated depending on the
underlying inpue device. Supporting non-terminal input is useful for
automated testing.

Windows emulation of VT100 uses "ESC [ E" for newline instead of
standard "ESC E", so a workaround is added.

Signed-off-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
---
 config/meson.build                      |   2 +
 lib/librte_cmdline/cmdline.c            |   5 +
 lib/librte_cmdline/cmdline_os_windows.c | 207 ++++++++++++++++++++++++
 lib/librte_cmdline/cmdline_private.h    |  15 ++
 lib/librte_cmdline/cmdline_socket.c     |   4 +
 lib/librte_cmdline/cmdline_vt100.h      |   4 +
 lib/librte_cmdline/meson.build          |   4 +-
 lib/meson.build                         |   1 +
 8 files changed, 241 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_cmdline/cmdline_os_windows.c

diff --git a/config/meson.build b/config/meson.build
index cff8b33dd..2d1b6fab2 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -270,6 +270,8 @@ if is_windows
 		add_project_arguments('-D__USE_MINGW_ANSI_STDIO', language: 'c')
 	endif
 
+	add_project_link_arguments('-lws2_32', language: 'c')
+
 	# Contrary to docs, VirtualAlloc2() is exported by mincore.lib
 	# in Windows SDK, while MinGW exports it by advapi32.a.
 	if is_ms_linker
diff --git a/lib/librte_cmdline/cmdline.c b/lib/librte_cmdline/cmdline.c
index 00b9e6b2e..c0ddb5f23 100644
--- a/lib/librte_cmdline/cmdline.c
+++ b/lib/librte_cmdline/cmdline.c
@@ -13,9 +13,14 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <netinet/in.h>
+#include <unistd.h>
 
 #include <rte_string_fns.h>
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#define write _write
+#endif
+
 #include "cmdline.h"
 #include "cmdline_private.h"
 
diff --git a/lib/librte_cmdline/cmdline_os_windows.c b/lib/librte_cmdline/cmdline_os_windows.c
new file mode 100644
index 000000000..9736f6531
--- /dev/null
+++ b/lib/librte_cmdline/cmdline_os_windows.c
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Dmitry Kozlyuk
+ */
+
+#include <io.h>
+
+#include <rte_os.h>
+
+#include "cmdline_private.h"
+
+/* Missing from some MinGW-w64 distributions. */
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
+#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
+#endif
+
+void
+terminal_adjust(struct terminal *oldterm)
+{
+	HANDLE handle;
+	DWORD mode;
+
+	ZeroMemory(oldterm, sizeof(*oldterm));
+
+	/* Detect console input, set it up and make it emulate VT100. */
+	handle = GetStdHandle(STD_INPUT_HANDLE);
+	if (GetConsoleMode(handle, &mode)) {
+		oldterm->is_console_input = 1;
+		oldterm->input_mode = mode;
+
+		mode &= ~(
+			ENABLE_LINE_INPUT |      /* no line buffering */
+			ENABLE_ECHO_INPUT |      /* no echo */
+			ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
+			ENABLE_MOUSE_INPUT |     /* no mouse events */
+			ENABLE_WINDOW_INPUT);    /* no window resize events */
+		mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+		SetConsoleMode(handle, mode);
+	}
+
+	/* Detect console output and make it emulate VT100. */
+	handle = GetStdHandle(STD_OUTPUT_HANDLE);
+	if (GetConsoleMode(handle, &mode)) {
+		oldterm->is_console_output = 1;
+		oldterm->output_mode = mode;
+
+		mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
+		mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+		SetConsoleMode(handle, mode);
+	}
+}
+
+void
+terminal_restore(const struct terminal *oldterm)
+{
+	if (oldterm->is_console_input) {
+		HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
+		SetConsoleMode(handle, oldterm->input_mode);
+	}
+
+	if (oldterm->is_console_output) {
+		HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
+		SetConsoleMode(handle, oldterm->output_mode);
+	}
+}
+
+static int
+cmdline_is_key_down(const INPUT_RECORD *record)
+{
+	return (record->EventType == KEY_EVENT) &&
+		record->Event.KeyEvent.bKeyDown;
+}
+
+static int
+cmdline_poll_char_console(HANDLE handle)
+{
+	INPUT_RECORD record;
+	DWORD events;
+
+	if (!PeekConsoleInput(handle, &record, 1, &events)) {
+		/* Simulate poll(3) behavior on EOF. */
+		return (GetLastError() == ERROR_HANDLE_EOF) ? 1 : -1;
+	}
+
+	if ((events == 0) || !cmdline_is_key_down(&record))
+		return 0;
+
+	return 1;
+}
+
+static int
+cmdline_poll_char_file(struct cmdline *cl, HANDLE handle)
+{
+	DWORD type = GetFileType(handle);
+
+	/* Since console is handled by cmdline_poll_char_console(),
+	 * this is either a serial port or input handle had been replaced.
+	 */
+	if (type == FILE_TYPE_CHAR)
+		return cmdline_poll_char_console(handle);
+
+	/* PeekNamedPipe() can handle all pipes and also sockets. */
+	if (type == FILE_TYPE_PIPE) {
+		DWORD bytes_avail;
+		if (!PeekNamedPipe(handle, NULL, 0, NULL, &bytes_avail, NULL))
+			return (GetLastError() == ERROR_BROKEN_PIPE) ? 1 : -1;
+		return bytes_avail ? 1 : 0;
+	}
+
+	/* There is no straightforward way to peek a file in Windows
+	 * I/O model. Read the byte, if it is not the end of file,
+	 * buffer it for subsequent read. This will not work with
+	 * a file being appended and probably some other edge cases.
+	 */
+	if (type == FILE_TYPE_DISK) {
+		char c;
+		int ret;
+
+		ret = _read(cl->s_in, &c, sizeof(c));
+		if (ret == 1) {
+			cl->repeat_count = 1;
+			cl->repeated_char = c;
+		}
+		return ret;
+	}
+
+	/* GetFileType() failed or file of unknown type,
+	 * which we do not know how to peek anyway.
+	 */
+	return -1;
+}
+
+int
+cmdline_poll_char(struct cmdline *cl)
+{
+	HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
+	return cl->oldterm.is_console_input ?
+		cmdline_poll_char_console(handle) :
+		cmdline_poll_char_file(cl, handle);
+}
+
+ssize_t
+cmdline_read_char(struct cmdline *cl, char *c)
+{
+	HANDLE handle;
+	INPUT_RECORD record;
+	KEY_EVENT_RECORD *key;
+	DWORD events;
+
+	if (!cl->oldterm.is_console_input)
+		return _read(cl->s_in, c, 1);
+
+	/* Return repeated strokes from previous event. */
+	if (cl->repeat_count > 0) {
+		*c = cl->repeated_char;
+		cl->repeat_count--;
+		return 1;
+	}
+
+	handle = (HANDLE)_get_osfhandle(cl->s_in);
+	key = &record.Event.KeyEvent;
+	do {
+		if (!ReadConsoleInput(handle, &record, 1, &events)) {
+			if (GetLastError() == ERROR_HANDLE_EOF) {
+				*c = EOF;
+				return 0;
+			}
+			return -1;
+		}
+	} while (!cmdline_is_key_down(&record));
+
+	*c = key->uChar.AsciiChar;
+
+	/* Save repeated strokes from a single event. */
+	if (key->wRepeatCount > 1) {
+		cl->repeated_char = *c;
+		cl->repeat_count = key->wRepeatCount - 1;
+	}
+
+	return 1;
+}
+
+int
+cmdline_vdprintf(int fd, const char *format, va_list op)
+{
+	int copy, ret;
+	FILE *file;
+
+	copy = _dup(fd);
+	if (copy < 0)
+		return -1;
+
+	file = _fdopen(copy, "a");
+	if (file == NULL) {
+		_close(copy);
+		return -1;
+	}
+
+	ret = vfprintf(file, format, op);
+
+	fclose(file); /* also closes copy */
+
+	return ret;
+}
diff --git a/lib/librte_cmdline/cmdline_private.h b/lib/librte_cmdline/cmdline_private.h
index 338d3d55c..1e05ec376 100644
--- a/lib/librte_cmdline/cmdline_private.h
+++ b/lib/librte_cmdline/cmdline_private.h
@@ -5,7 +5,11 @@
 #ifndef _CMDLINE_PRIVATE_H_
 #define _CMDLINE_PRIVATE_H_
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#include <rte_windows.h>
+#else
 #include <termios.h>
+#endif
 
 #include <stdarg.h>
 
@@ -15,7 +19,14 @@
 #include <cmdline_parse.h>
 
 struct terminal {
+#ifndef RTE_EXEC_ENV_WINDOWS
 	struct termios termios;
+#else
+	DWORD input_mode;
+	DWORD output_mode;
+	int is_console_input;
+	int is_console_output;
+#endif
 };
 
 /* Disable buffering and echoing, save previous settings to oldterm. */
@@ -31,6 +42,10 @@ struct cmdline {
 	struct rdline rdl;
 	char prompt[RDLINE_PROMPT_SIZE];
 	struct terminal oldterm;
+#ifdef RTE_EXEC_ENV_WINDOWS
+	char repeated_char;
+	WORD repeat_count;
+#endif
 };
 
 /* Check if a single character can be read from input. */
diff --git a/lib/librte_cmdline/cmdline_socket.c b/lib/librte_cmdline/cmdline_socket.c
index e73666f15..c5f483413 100644
--- a/lib/librte_cmdline/cmdline_socket.c
+++ b/lib/librte_cmdline/cmdline_socket.c
@@ -16,6 +16,10 @@
 #include "cmdline_private.h"
 #include "cmdline_socket.h"
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#define open _open
+#endif
+
 struct cmdline *
 cmdline_file_new(cmdline_parse_ctx_t *ctx, const char *prompt, const char *path)
 {
diff --git a/lib/librte_cmdline/cmdline_vt100.h b/lib/librte_cmdline/cmdline_vt100.h
index e33e67ed8..be9ae8e1c 100644
--- a/lib/librte_cmdline/cmdline_vt100.h
+++ b/lib/librte_cmdline/cmdline_vt100.h
@@ -31,7 +31,11 @@ extern "C" {
 #define vt100_multi_right  "\033\133%uC"
 #define vt100_multi_left   "\033\133%uD"
 #define vt100_suppr        "\033\133\063\176"
+#ifndef RTE_EXEC_ENV_WINDOWS
 #define vt100_home         "\033M\033E"
+#else
+#define vt100_home         "\033M\033[E"
+#endif
 #define vt100_word_left    "\033\142"
 #define vt100_word_right   "\033\146"
 
diff --git a/lib/librte_cmdline/meson.build b/lib/librte_cmdline/meson.build
index 5c9e8886d..5009b3354 100644
--- a/lib/librte_cmdline/meson.build
+++ b/lib/librte_cmdline/meson.build
@@ -25,7 +25,9 @@ headers = files('cmdline.h',
 	'cmdline_cirbuf.h',
 	'cmdline_parse_portlist.h')
 
-if not is_windows
+if is_windows
+	sources += files('cmdline_os_windows.c')
+else
 	sources += files('cmdline_os_unix.c')
 endif
 
diff --git a/lib/meson.build b/lib/meson.build
index 6bbaf242a..8430057c6 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -41,6 +41,7 @@ if is_windows
 		'eal',
 		'ring',
 		'mempool', 'mbuf', 'pci', 'net',
+		'cmdline',
 	] # only supported libraries for windows
 endif
 
-- 
2.25.4


  parent reply	other threads:[~2020-07-30 21:08 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-20 21:05 [dpdk-dev] [PATCH 0/7] cmdline: support Windows Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 1/7] cmdline: make implementation opaque Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 2/7] cmdline: add internal wrappers for terminal handling Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 3/7] cmdline: add internal wrappers for character input Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 4/7] cmdline: add internal wrapper for vdprintf Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 5/7] eal/windows: improve compatibility networking headers Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 6/7] cmdline: support Windows Dmitry Kozlyuk
2020-06-28 14:20   ` Fady Bader
2020-06-29  6:23     ` Ranjit Menon
2020-06-29  7:42       ` Dmitry Kozlyuk
2020-06-29  8:12         ` Tal Shnaiderman
2020-06-29 23:56           ` Dmitry Kozlyuk
2020-07-08  1:09             ` Dmitry Kozlyuk
2020-06-20 21:05 ` [dpdk-dev] [PATCH 7/7] examples/cmdline: build on Windows Dmitry Kozlyuk
2020-07-17 22:16 ` [dpdk-dev] [PATCH 0/7] cmdline: support Windows Narcisa Ana Maria Vasile
2020-07-30 18:08 ` Kadam, Pallavi
2020-07-30 21:06 ` [dpdk-dev] [PATCH v2 " Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 1/7] cmdline: make implementation opaque Dmitry Kozlyuk
2020-08-05  9:31     ` Kinsella, Ray
2020-08-05 11:17       ` Dmitry Kozlyuk
2020-09-30  8:11         ` Kinsella, Ray
2020-09-30 15:26           ` Dmitry Kozlyuk
2020-09-17 13:34     ` Olivier Matz
2020-09-17 17:05       ` Stephen Hemminger
2020-09-18  8:33         ` Bruce Richardson
2020-09-18 12:13           ` Ferruh Yigit
2020-09-18 13:23         ` Kinsella, Ray
2020-09-17 23:13       ` Dmitry Kozlyuk
2020-09-18 13:31       ` Kinsella, Ray
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 2/7] cmdline: add internal wrappers for terminal handling Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 3/7] cmdline: add internal wrappers for character input Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 4/7] cmdline: add internal wrapper for vdprintf Dmitry Kozlyuk
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 5/7] eal/windows: improve compatibility networking headers Dmitry Kozlyuk
2020-07-30 21:06   ` Dmitry Kozlyuk [this message]
2020-07-30 21:06   ` [dpdk-dev] [PATCH v2 7/7] examples/cmdline: build on Windows Dmitry Kozlyuk
2020-09-26  1:33     ` [dpdk-dev] [EXTERNAL] " Narcisa Ana Maria Vasile
2020-09-26  6:03       ` Khoa To
2020-09-28 21:50   ` [dpdk-dev] [PATCH v3 0/7] cmdline: support Windows Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 1/7] cmdline: make implementation logically opaque Dmitry Kozlyuk
2020-09-30  8:12       ` Kinsella, Ray
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 2/7] cmdline: add internal wrappers for terminal handling Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 3/7] cmdline: add internal wrappers for character input Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 4/7] cmdline: add internal wrapper for vdprintf Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 5/7] eal/windows: improve compatibility networking headers Dmitry Kozlyuk
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 6/7] cmdline: support Windows Dmitry Kozlyuk
2020-10-14 22:31       ` Thomas Monjalon
2020-09-28 21:50     ` [dpdk-dev] [PATCH v3 7/7] examples/cmdline: build on Windows Dmitry Kozlyuk
2020-10-14 22:33       ` Thomas Monjalon
2020-10-05 15:33     ` [dpdk-dev] [PATCH v3 0/7] cmdline: support Windows Olivier Matz
2020-10-14 22:41       ` Thomas Monjalon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200730210652.14568-7-dmitry.kozliuk@gmail.com \
    --to=dmitry.kozliuk@gmail.com \
    --cc=Narcisa.Vasile@microsoft.com \
    --cc=dev@dpdk.org \
    --cc=dmitrym@microsoft.com \
    --cc=fady@mellanox.com \
    --cc=olivier.matz@6wind.com \
    --cc=pallavi.kadam@intel.com \
    --cc=talshn@mellanox.com \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.