qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH V5] Add stdio char device on windows
@ 2011-10-06 14:37 Fabien Chouteau
  2011-10-12 14:47 ` Fabien Chouteau
  2011-10-23 20:05 ` Blue Swirl
  0 siblings, 2 replies; 4+ messages in thread
From: Fabien Chouteau @ 2011-10-06 14:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: blauwirbel, pbonzini, yann.fc.quelen, mars

Simple implementation of an stdio char device on Windows.

Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 qemu-char.c |  227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 225 insertions(+), 2 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 09d2309..b9381be 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -538,6 +538,9 @@ int send_all(int fd, const void *_buf, int len1)
 }
 #endif /* !_WIN32 */
 
+#define STDIO_MAX_CLIENTS 1
+static int stdio_nb_clients;
+
 #ifndef _WIN32
 
 typedef struct {
@@ -545,8 +548,6 @@ typedef struct {
     int max_size;
 } FDCharDriver;
 
-#define STDIO_MAX_CLIENTS 1
-static int stdio_nb_clients = 0;
 
 static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
@@ -1451,6 +1452,8 @@ static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr)
 
 #else /* _WIN32 */
 
+static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
+
 typedef struct {
     int max_size;
     HANDLE hcom, hrecv, hsend;
@@ -1459,6 +1462,14 @@ typedef struct {
     DWORD len;
 } WinCharState;
 
+typedef struct {
+    HANDLE  hStdIn;
+    HANDLE  hInputReadyEvent;
+    HANDLE  hInputDoneEvent;
+    HANDLE  hInputThread;
+    uint8_t win_stdio_buf;
+} WinStdioCharState;
+
 #define NSENDBUF 2048
 #define NRECVBUF 2048
 #define MAXCONNECT 1
@@ -1809,6 +1820,217 @@ static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr)
 
     return qemu_chr_open_win_file(fd_out, _chr);
 }
+
+static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+    DWORD   dwSize;
+    int     len1;
+
+    len1 = len;
+
+    while (len1 > 0) {
+        if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
+            break;
+        }
+        buf  += dwSize;
+        len1 -= dwSize;
+    }
+
+    return len - len1;
+}
+
+static void win_stdio_wait_func(void *opaque)
+{
+    CharDriverState   *chr   = opaque;
+    WinStdioCharState *stdio = chr->opaque;
+    INPUT_RECORD       buf[4];
+    int                ret;
+    DWORD              dwSize;
+    int                i;
+
+    ret = ReadConsoleInput(stdio->hStdIn, buf, sizeof(buf) / sizeof(*buf),
+                           &dwSize);
+
+    if (!ret) {
+        /* Avoid error storm */
+        qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
+        return;
+    }
+
+    for (i = 0; i < dwSize; 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_be_can_write(chr)) {
+                        uint8_t c = kev->uChar.AsciiChar;
+                        qemu_chr_be_write(chr, &c, 1);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static DWORD WINAPI win_stdio_thread(LPVOID param)
+{
+    CharDriverState   *chr   = param;
+    WinStdioCharState *stdio = chr->opaque;
+    int                ret;
+    DWORD              dwSize;
+
+    while (1) {
+
+        /* Wait for one byte */
+        ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
+
+        /* Exit in case of error, continue if nothing read */
+        if (!ret) {
+            break;
+        }
+        if (!dwSize) {
+            continue;
+        }
+
+        /* Some terminal emulator returns \r\n for Enter, just pass \n */
+        if (stdio->win_stdio_buf == '\r') {
+            continue;
+        }
+
+        /* Signal the main thread and wait until the byte was eaten */
+        if (!SetEvent(stdio->hInputReadyEvent)) {
+            break;
+        }
+        if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
+            != WAIT_OBJECT_0) {
+            break;
+        }
+    }
+
+    qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
+    return 0;
+}
+
+static void win_stdio_thread_wait_func(void *opaque)
+{
+    CharDriverState   *chr   = opaque;
+    WinStdioCharState *stdio = chr->opaque;
+
+    if (qemu_chr_be_can_write(chr)) {
+        qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
+    }
+
+    SetEvent(stdio->hInputDoneEvent);
+}
+
+static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
+{
+    WinStdioCharState *stdio  = chr->opaque;
+    DWORD              dwMode = 0;
+
+    GetConsoleMode(stdio->hStdIn, &dwMode);
+
+    if (echo) {
+        SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
+    } else {
+        SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
+    }
+}
+
+static void win_stdio_close(CharDriverState *chr)
+{
+    WinStdioCharState *stdio = chr->opaque;
+
+    if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
+        CloseHandle(stdio->hInputReadyEvent);
+    }
+    if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
+        CloseHandle(stdio->hInputDoneEvent);
+    }
+    if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
+        TerminateThread(stdio->hInputThread, 0);
+    }
+
+    g_free(chr->opaque);
+    g_free(chr);
+    stdio_nb_clients--;
+}
+
+static int qemu_chr_open_win_stdio(QemuOpts *opts, CharDriverState **_chr)
+{
+    CharDriverState   *chr;
+    WinStdioCharState *stdio;
+    DWORD              dwMode;
+    int                is_console = 0;
+
+    if (stdio_nb_clients >= STDIO_MAX_CLIENTS
+        || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) {
+        return -EIO;
+    }
+
+    chr   = g_malloc0(sizeof(CharDriverState));
+    stdio = g_malloc0(sizeof(WinStdioCharState));
+
+    stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
+    if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "cannot open stdio: invalid handle\n");
+        exit(1);
+    }
+
+    is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
+
+    chr->opaque    = stdio;
+    chr->chr_write = win_stdio_write;
+    chr->chr_close = win_stdio_close;
+
+    if (stdio_nb_clients == 0) {
+        if (is_console) {
+            if (qemu_add_wait_object(stdio->hStdIn,
+                                     win_stdio_wait_func, chr)) {
+                fprintf(stderr, "qemu_add_wait_object: failed\n");
+            }
+        } else {
+            DWORD   dwId;
+
+            stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+            stdio->hInputDoneEvent  = CreateEvent(NULL, FALSE, FALSE, NULL);
+            stdio->hInputThread     = CreateThread(NULL, 0, win_stdio_thread,
+                                            chr, 0, &dwId);
+
+            if (stdio->hInputThread == INVALID_HANDLE_VALUE
+                || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
+                || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
+                fprintf(stderr, "cannot create stdio thread or event\n");
+                exit(1);
+            }
+            if (qemu_add_wait_object(stdio->hInputReadyEvent,
+                                     win_stdio_thread_wait_func, chr)) {
+                fprintf(stderr, "qemu_add_wait_object: failed\n");
+            }
+        }
+    }
+
+    dwMode |= ENABLE_LINE_INPUT;
+
+    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 */
+        dwMode |= ENABLE_PROCESSED_INPUT;
+    }
+
+    SetConsoleMode(stdio->hStdIn, dwMode);
+
+    chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
+    qemu_chr_fe_set_echo(chr, false);
+
+    *_chr = chr;
+
+    return 0;
+}
 #endif /* !_WIN32 */
 
 /***********************************************************/
@@ -2519,6 +2741,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 },
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH V5] Add stdio char device on windows
  2011-10-06 14:37 [Qemu-devel] [PATCH V5] Add stdio char device on windows Fabien Chouteau
@ 2011-10-12 14:47 ` Fabien Chouteau
  2011-10-20 15:50   ` Fabien Chouteau
  2011-10-23 20:05 ` Blue Swirl
  1 sibling, 1 reply; 4+ messages in thread
From: Fabien Chouteau @ 2011-10-12 14:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: blauwirbel, pbonzini, yann.fc.quelen, mars

On 06/10/2011 16:37, Fabien Chouteau wrote:
> Simple implementation of an stdio char device on Windows.
> 

Any comments?

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH V5] Add stdio char device on windows
  2011-10-12 14:47 ` Fabien Chouteau
@ 2011-10-20 15:50   ` Fabien Chouteau
  0 siblings, 0 replies; 4+ messages in thread
From: Fabien Chouteau @ 2011-10-20 15:50 UTC (permalink / raw)
  To: qemu-devel, Anthony Liguori; +Cc: blauwirbel, pbonzini, yann.fc.quelen, mars

On 12/10/2011 16:47, Fabien Chouteau wrote:
> On 06/10/2011 16:37, Fabien Chouteau wrote:
>> Simple implementation of an stdio char device on Windows.
>>
> 
> Any comments?
> 

Anthony, can you apply this patch please?

Thanks in advance,

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH V5] Add stdio char device on windows
  2011-10-06 14:37 [Qemu-devel] [PATCH V5] Add stdio char device on windows Fabien Chouteau
  2011-10-12 14:47 ` Fabien Chouteau
@ 2011-10-23 20:05 ` Blue Swirl
  1 sibling, 0 replies; 4+ messages in thread
From: Blue Swirl @ 2011-10-23 20:05 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: pbonzini, yann.fc.quelen, mars, qemu-devel

Thanks, applied.

On Thu, Oct 6, 2011 at 14:37, Fabien Chouteau <chouteau@adacore.com> wrote:
> Simple implementation of an stdio char device on Windows.
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  qemu-char.c |  227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 225 insertions(+), 2 deletions(-)
>
> diff --git a/qemu-char.c b/qemu-char.c
> index 09d2309..b9381be 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -538,6 +538,9 @@ int send_all(int fd, const void *_buf, int len1)
>  }
>  #endif /* !_WIN32 */
>
> +#define STDIO_MAX_CLIENTS 1
> +static int stdio_nb_clients;
> +
>  #ifndef _WIN32
>
>  typedef struct {
> @@ -545,8 +548,6 @@ typedef struct {
>     int max_size;
>  } FDCharDriver;
>
> -#define STDIO_MAX_CLIENTS 1
> -static int stdio_nb_clients = 0;
>
>  static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>  {
> @@ -1451,6 +1452,8 @@ static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr)
>
>  #else /* _WIN32 */
>
> +static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
> +
>  typedef struct {
>     int max_size;
>     HANDLE hcom, hrecv, hsend;
> @@ -1459,6 +1462,14 @@ typedef struct {
>     DWORD len;
>  } WinCharState;
>
> +typedef struct {
> +    HANDLE  hStdIn;
> +    HANDLE  hInputReadyEvent;
> +    HANDLE  hInputDoneEvent;
> +    HANDLE  hInputThread;
> +    uint8_t win_stdio_buf;
> +} WinStdioCharState;
> +
>  #define NSENDBUF 2048
>  #define NRECVBUF 2048
>  #define MAXCONNECT 1
> @@ -1809,6 +1820,217 @@ static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr)
>
>     return qemu_chr_open_win_file(fd_out, _chr);
>  }
> +
> +static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
> +{
> +    HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
> +    DWORD   dwSize;
> +    int     len1;
> +
> +    len1 = len;
> +
> +    while (len1 > 0) {
> +        if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
> +            break;
> +        }
> +        buf  += dwSize;
> +        len1 -= dwSize;
> +    }
> +
> +    return len - len1;
> +}
> +
> +static void win_stdio_wait_func(void *opaque)
> +{
> +    CharDriverState   *chr   = opaque;
> +    WinStdioCharState *stdio = chr->opaque;
> +    INPUT_RECORD       buf[4];
> +    int                ret;
> +    DWORD              dwSize;
> +    int                i;
> +
> +    ret = ReadConsoleInput(stdio->hStdIn, buf, sizeof(buf) / sizeof(*buf),
> +                           &dwSize);
> +
> +    if (!ret) {
> +        /* Avoid error storm */
> +        qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
> +        return;
> +    }
> +
> +    for (i = 0; i < dwSize; 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_be_can_write(chr)) {
> +                        uint8_t c = kev->uChar.AsciiChar;
> +                        qemu_chr_be_write(chr, &c, 1);
> +                    }
> +                }
> +            }
> +        }
> +    }
> +}
> +
> +static DWORD WINAPI win_stdio_thread(LPVOID param)
> +{
> +    CharDriverState   *chr   = param;
> +    WinStdioCharState *stdio = chr->opaque;
> +    int                ret;
> +    DWORD              dwSize;
> +
> +    while (1) {
> +
> +        /* Wait for one byte */
> +        ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
> +
> +        /* Exit in case of error, continue if nothing read */
> +        if (!ret) {
> +            break;
> +        }
> +        if (!dwSize) {
> +            continue;
> +        }
> +
> +        /* Some terminal emulator returns \r\n for Enter, just pass \n */
> +        if (stdio->win_stdio_buf == '\r') {
> +            continue;
> +        }
> +
> +        /* Signal the main thread and wait until the byte was eaten */
> +        if (!SetEvent(stdio->hInputReadyEvent)) {
> +            break;
> +        }
> +        if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
> +            != WAIT_OBJECT_0) {
> +            break;
> +        }
> +    }
> +
> +    qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
> +    return 0;
> +}
> +
> +static void win_stdio_thread_wait_func(void *opaque)
> +{
> +    CharDriverState   *chr   = opaque;
> +    WinStdioCharState *stdio = chr->opaque;
> +
> +    if (qemu_chr_be_can_write(chr)) {
> +        qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
> +    }
> +
> +    SetEvent(stdio->hInputDoneEvent);
> +}
> +
> +static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
> +{
> +    WinStdioCharState *stdio  = chr->opaque;
> +    DWORD              dwMode = 0;
> +
> +    GetConsoleMode(stdio->hStdIn, &dwMode);
> +
> +    if (echo) {
> +        SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
> +    } else {
> +        SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
> +    }
> +}
> +
> +static void win_stdio_close(CharDriverState *chr)
> +{
> +    WinStdioCharState *stdio = chr->opaque;
> +
> +    if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
> +        CloseHandle(stdio->hInputReadyEvent);
> +    }
> +    if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
> +        CloseHandle(stdio->hInputDoneEvent);
> +    }
> +    if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
> +        TerminateThread(stdio->hInputThread, 0);
> +    }
> +
> +    g_free(chr->opaque);
> +    g_free(chr);
> +    stdio_nb_clients--;
> +}
> +
> +static int qemu_chr_open_win_stdio(QemuOpts *opts, CharDriverState **_chr)
> +{
> +    CharDriverState   *chr;
> +    WinStdioCharState *stdio;
> +    DWORD              dwMode;
> +    int                is_console = 0;
> +
> +    if (stdio_nb_clients >= STDIO_MAX_CLIENTS
> +        || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) {
> +        return -EIO;
> +    }
> +
> +    chr   = g_malloc0(sizeof(CharDriverState));
> +    stdio = g_malloc0(sizeof(WinStdioCharState));
> +
> +    stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
> +    if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
> +        fprintf(stderr, "cannot open stdio: invalid handle\n");
> +        exit(1);
> +    }
> +
> +    is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
> +
> +    chr->opaque    = stdio;
> +    chr->chr_write = win_stdio_write;
> +    chr->chr_close = win_stdio_close;
> +
> +    if (stdio_nb_clients == 0) {
> +        if (is_console) {
> +            if (qemu_add_wait_object(stdio->hStdIn,
> +                                     win_stdio_wait_func, chr)) {
> +                fprintf(stderr, "qemu_add_wait_object: failed\n");
> +            }
> +        } else {
> +            DWORD   dwId;
> +
> +            stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
> +            stdio->hInputDoneEvent  = CreateEvent(NULL, FALSE, FALSE, NULL);
> +            stdio->hInputThread     = CreateThread(NULL, 0, win_stdio_thread,
> +                                            chr, 0, &dwId);
> +
> +            if (stdio->hInputThread == INVALID_HANDLE_VALUE
> +                || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
> +                || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
> +                fprintf(stderr, "cannot create stdio thread or event\n");
> +                exit(1);
> +            }
> +            if (qemu_add_wait_object(stdio->hInputReadyEvent,
> +                                     win_stdio_thread_wait_func, chr)) {
> +                fprintf(stderr, "qemu_add_wait_object: failed\n");
> +            }
> +        }
> +    }
> +
> +    dwMode |= ENABLE_LINE_INPUT;
> +
> +    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 */
> +        dwMode |= ENABLE_PROCESSED_INPUT;
> +    }
> +
> +    SetConsoleMode(stdio->hStdIn, dwMode);
> +
> +    chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
> +    qemu_chr_fe_set_echo(chr, false);
> +
> +    *_chr = chr;
> +
> +    return 0;
> +}
>  #endif /* !_WIN32 */
>
>  /***********************************************************/
> @@ -2519,6 +2741,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 },
> --
> 1.7.4.1
>
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2011-10-23 20:05 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-10-06 14:37 [Qemu-devel] [PATCH V5] Add stdio char device on windows Fabien Chouteau
2011-10-12 14:47 ` Fabien Chouteau
2011-10-20 15:50   ` Fabien Chouteau
2011-10-23 20:05 ` Blue Swirl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).