Simple implementation of an stdio char device on Windows.
Signed-off-by: Fabien Chouteau<chout...@adacore.com>
---
qemu-char.c | 171
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 171 insertions(+), 0 deletions(-)
diff --git a/qemu-char.c b/qemu-char.c
index edc9ad6..c18e668 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -1436,6 +1436,11 @@ static CharDriverState
*qemu_chr_open_pp(QemuOpts *opts)
#else /* _WIN32 */
+#define STDIO_MAX_CLIENTS 2
+
+static void win_stdio_wait_func(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ INPUT_RECORD buf[4];
+ int ret;
+ DWORD size;
+ int i;
+
+ ret = ReadConsoleInput(Input, buf, sizeof(buf)/sizeof(*buf),&size);
+
+ if (!ret) {
+ /* Avoid error storm */
+ qemu_del_wait_object(Input, NULL, NULL);
+ return;
+ }
+
+ for (i = 0; i< size; i++) {
+ KEY_EVENT_RECORD *kev =&buf[i].Event.KeyEvent;
+
+ if (buf[i].EventType == KEY_EVENT&& kev->bKeyDown) {
+ int j;
+ if (kev->uChar.AsciiChar != 0) {
+ for (j = 0; j< kev->wRepeatCount; j++)
+ if (qemu_chr_can_read(chr)) {
+ uint8_t c = kev->uChar.AsciiChar;
+ qemu_chr_read(chr,&c, 1);
+ }
+ }
+ }
+ }
+}
+
+static HANDLE InputReadyEvent;
+static HANDLE InputDoneEvent;
+static uint8_t InputBuf;
+
+static DWORD WINAPI win_stdio_thread(LPVOID param)
+{
+ int ret;
+ DWORD size;
+
+ while (1) {
+
+ /* Wait for one byte */
+ ret = ReadFile(Input,&InputBuf, 1,&size, NULL);
+
+ /* Exit in case of error, continue if nothing read */
+ if (!ret) {
+ break;
+ }
+ if (!size) {
+ continue;
+ }
+
+ /* Some terminal emulator returns \r\n for Enter, just pass
\n */
+ if (InputBuf == '\r') {
+ continue;
+ }
+
+ /* Signal the main thread and wait until the byte was eaten */
+ if (!SetEvent(InputReadyEvent)) {
+ break;
+ }
+ if (WaitForSingleObject(InputDoneEvent, INFINITE) !=
WAIT_OBJECT_0) {
+ break;
+ }
+ }
+
+ qemu_del_wait_object(InputReadyEvent, NULL, NULL);
+ return 0;
+}
+
+static void win_stdio_thread_wait_func(void *opaque)
+{
+ CharDriverState *chr = opaque;
+
+ if (qemu_chr_can_read(chr)) {
+ qemu_chr_read(chr,&InputBuf, 1);
+ }
+
+ SetEvent(InputDoneEvent);
+}
+
+static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts)
+{
+ CharDriverState *chr;
+ DWORD mode;
+ int is_console = 0;
+
+ Input = GetStdHandle(STD_INPUT_HANDLE);
+ if (Input == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "cannot open stdio: invalid handle\n");
+ exit(1);
+ }
+
+ is_console = GetConsoleMode(Input,&mode) != 0;
+
+ if (stdio_nb_clients>= STDIO_MAX_CLIENTS
+ || ((display_type != DT_NOGRAPHIC)&& (stdio_nb_clients !=
0))) {
+ return NULL;
+ }
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr) {
+ return NULL;
+ }
+
+ chr->chr_write = win_stdio_write;
+
+ if (stdio_nb_clients == 0) {
+ if (is_console) {
+ if (qemu_add_wait_object(InputReadyEvent,
+ win_stdio_thread_wait_func,
chr)) {
+ fprintf(stderr, "qemu_add_wait_object: failed\n");
+ }
+ } else {
+ DWORD id;
+ HANDLE *InputThread;
+
+ InputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ InputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ InputThread = CreateThread(NULL, 0, win_stdio_thread,
chr, 0,&id);
+
+ if (InputThread == INVALID_HANDLE_VALUE
+ || InputReadyEvent == INVALID_HANDLE_VALUE
+ || InputDoneEvent == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "cannot create stdio thread or
event\n");
+ exit(1);
+ }
+ if (qemu_add_wait_object(Input,
win_stdio_thread_wait_func, chr)) {
+ fprintf(stderr, "qemu_add_wait_object: failed\n");
+ }
+ }
+ }
+
+ stdio_clients[stdio_nb_clients++] = chr;
+ if (stdio_nb_clients == 1 && is_console) {
+ /* set the terminal in raw mode */
+ /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
+ SetConsoleMode(Input, ENABLE_PROCESSED_INPUT);
+ }
+ return chr;
+}
#endif /* !_WIN32 */
/***********************************************************/
@@ -2477,6 +2647,7 @@ static const struct {
{ .name = "pipe", .open = qemu_chr_open_win_pipe },
{ .name = "console", .open = qemu_chr_open_win_con },
{ .name = "serial", .open = qemu_chr_open_win },
+ { .name = "stdio", .open = qemu_chr_open_win_stdio },
#else
{ .name = "file", .open = qemu_chr_open_file_out },
{ .name = "pipe", .open = qemu_chr_open_pipe },