[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH V5] Add stdio char device on windows
From: |
Blue Swirl |
Subject: |
Re: [Qemu-devel] [PATCH V5] Add stdio char device on windows |
Date: |
Sun, 23 Oct 2011 20:05:16 +0000 |
Thanks, applied.
On Thu, Oct 6, 2011 at 14:37, Fabien Chouteau <address@hidden> wrote:
> Simple implementation of an stdio char device on Windows.
>
> Signed-off-by: Fabien Chouteau <address@hidden>
> ---
> 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
>
>