Patchwork [RFC] Add stdio char device on windows

login
register
mail settings
Submitter Fabien Chouteau
Date Jan. 31, 2011, 5:09 p.m.
Message ID <5cef88f5cfc195f16583a0dda5cf16670cc0c0db.1296493772.git.chouteau@adacore.com>
Download mbox | patch
Permalink /patch/81187/
State New
Headers show

Comments

Fabien Chouteau - Jan. 31, 2011, 5:09 p.m.
Simple implementation of an stdio char device on Windows.

Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 qemu-char.c |  171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 171 insertions(+), 0 deletions(-)
Paolo Bonzini - Jan. 31, 2011, 5:43 p.m.
On 01/31/2011 06:09 PM, Fabien Chouteau wrote:
> Simple implementation of an stdio char device on Windows.
>
> Signed-off-by: Fabien Chouteau<chouteau@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

Why 2?  If it were 1, you could use a single definition for Unix and Win32.

> +
> +static int stdio_nb_clients;
> +static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
> +
>   typedef struct {
>       int max_size;
>       HANDLE hcom, hrecv, hsend;
> @@ -1788,6 +1793,171 @@ static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts)
>
>       return qemu_chr_open_win_file(fd_out);
>   }
> +
> +static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
> +{
> +    HANDLE *Output = GetStdHandle(STD_OUTPUT_HANDLE);

Please use Hungarian notation for Windows objects, i.e. hStdOut.

> +    DWORD   size;
> +    int     len1;
> +
> +    len1 = len;
> +
> +    while (len1>  0) {
> +        if (!WriteFile(Output, buf, len1,&size, NULL)) {
> +            break;
> +        }
> +        buf  += size;
> +        len1 -= size;
> +    }
> +
> +    return len - len1;
> +}
> +
> +static HANDLE *Input;

and hStdIn

> +
> +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;

and hInputReadyEvent, hInputDoneEvent.  Stuff that is not Windows 
objects should use underscores as in win_stdio_buf.

> +
> +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 },

Otherwise looks good, can you wait for 
http://permalink.gmane.org/gmane.comp.emulators.qemu/88490 to be merged 
so that you can add the set_echo implementation too?

Thanks!

Paolo
Fabien Chouteau - Feb. 1, 2011, 11:24 a.m.
On 01/31/2011 06:43 PM, Paolo Bonzini wrote:
> On 01/31/2011 06:09 PM, Fabien Chouteau wrote:
>> Simple implementation of an stdio char device on Windows.
>>
>> Signed-off-by: Fabien Chouteau<chouteau@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
>
> Why 2?  If it were 1, you could use a single definition for Unix and 
> Win32.
>
>> +
>> +static int stdio_nb_clients;
>> +static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
>> +
>>   typedef struct {
>>       int max_size;
>>       HANDLE hcom, hrecv, hsend;
>> @@ -1788,6 +1793,171 @@ static CharDriverState 
>> *qemu_chr_open_win_file_out(QemuOpts *opts)
>>
>>       return qemu_chr_open_win_file(fd_out);
>>   }
>> +
>> +static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, 
>> int len)
>> +{
>> +    HANDLE *Output = GetStdHandle(STD_OUTPUT_HANDLE);
>
> Please use Hungarian notation for Windows objects, i.e. hStdOut.
>
>> +    DWORD   size;
>> +    int     len1;
>> +
>> +    len1 = len;
>> +
>> +    while (len1>  0) {
>> +        if (!WriteFile(Output, buf, len1,&size, NULL)) {
>> +            break;
>> +        }
>> +        buf  += size;
>> +        len1 -= size;
>> +    }
>> +
>> +    return len - len1;
>> +}
>> +
>> +static HANDLE *Input;
>
> and hStdIn
>
>> +
>> +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;
>
> and hInputReadyEvent, hInputDoneEvent.  Stuff that is not Windows 
> objects should use underscores as in win_stdio_buf.
>
>> +
>> +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 },
>
> Otherwise looks good, can you wait for 
> http://permalink.gmane.org/gmane.comp.emulators.qemu/88490 to be 
> merged so that you can add the set_echo implementation too?

OK, I can wait. When do you expect your patches to be ready?

Thanks for your comments,
Paolo Bonzini - Feb. 1, 2011, 12:39 p.m.
On 02/01/2011 12:24 PM, Fabien Chouteau wrote:
>> Otherwise looks good, can you wait for
>> http://permalink.gmane.org/gmane.comp.emulators.qemu/88490 to be
>> merged so that you can add the set_echo implementation too?
>
> OK, I can wait. When do you expect your patches to be ready?

They are, I'm waiting for someone to pick them.

Paolo

Patch

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 int stdio_nb_clients;
+static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
+
 typedef struct {
     int max_size;
     HANDLE hcom, hrecv, hsend;
@@ -1788,6 +1793,171 @@  static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts)
 
     return qemu_chr_open_win_file(fd_out);
 }
+
+static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    HANDLE *Output = GetStdHandle(STD_OUTPUT_HANDLE);
+    DWORD   size;
+    int     len1;
+
+    len1 = len;
+
+    while (len1 > 0) {
+        if (!WriteFile(Output, buf, len1, &size, NULL)) {
+            break;
+        }
+        buf  += size;
+        len1 -= size;
+    }
+
+    return len - len1;
+}
+
+static HANDLE *Input;
+
+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 },