I love the -curses feature but I really wanted to use it over a telnet: character device. This patch enables this and changes the syntax of the -curses argument to take a character device. 'stdio' is special cased to not go through a character device. The rest use a pty to proxy the data to the character device. curses insists on a pty so we limit this feature to everything but WIN32 since I don't believe Windows supports ptys.
Using this patch, you can now do something like '-curses telnet::1028,nowait'. To get the old behavior, just use '-curses stdio'. Signed-off-by: Anthony Liguori <[EMAIL PROTECTED]> diff --git a/console.h b/console.h index b8a5c6d..43e1284 100644 --- a/console.h +++ b/console.h @@ -141,7 +141,7 @@ int vnc_display_password(DisplayState *ds, const char *password); void do_info_vnc(void); /* curses.c */ -void curses_display_init(DisplayState *ds, int full_screen); +void curses_display_init(DisplayState *ds, CharDriverState *chr, int full_screen); /* x_keymap.c */ extern uint8_t _translate_keycode(const int key); diff --git a/curses.c b/curses.c index 87aa9b3..a020dd1 100644 --- a/curses.c +++ b/curses.c @@ -32,6 +32,7 @@ #include <signal.h> #include <sys/ioctl.h> #include <termios.h> +#include <pty.h> #endif #define FONT_HEIGHT 16 @@ -41,6 +42,7 @@ static console_ch_t screen[160 * 100]; static WINDOW *screenpad = NULL; static int width, height, gwidth, gheight, invalidate; static int px, py, sminx, sminy, smaxx, smaxy; +static int slave; static void curses_update(DisplayState *ds, int x, int y, int w, int h) { @@ -278,16 +280,102 @@ static void curses_atexit(void) curses_cleanup(NULL); } -static void curses_setup(void) +#ifndef _WIN32 +static void pty_read_proxy(void *opaque) +{ + CharDriverState *chr = opaque; + char buffer[4096]; + ssize_t len; + + do { + len = read(slave, buffer, sizeof(buffer)); + } while (len == -1 && errno == EINTR); + + if (len > 0) + qemu_chr_write(chr, buffer, len); +} + +static void chr_read_proxy(void *opaque, const uint8_t *buf, int size) +{ + ssize_t len; + size_t offset = 0; + + do { + len = write(slave, buf + offset, size - offset); + if (len == -1 && (errno == EINTR || errno == EAGAIN)) + continue; + if (len > 0) + offset += len; + } while (offset < size && len > 0); + + if (offset != size) + fprintf(stderr, "curses: short write to slave pty\n"); +} + +static int chr_can_read_proxy(void *opaque) +{ + return 1024; +} + +static void chr_event_proxy(void *opaque, int event) +{ + if (event == CHR_EVENT_RESET) + invalidate = 1; +} +#endif + +static void curses_setup(CharDriverState *chr) { int i, colour_default[8] = { COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, }; +#ifndef _WIN32 + if (chr) { + struct termios term; + FILE *fin, *fout; + SCREEN *screen; + int master; + + if (openpty(&master, &slave, NULL, NULL, NULL) == -1) { + fprintf(stderr, "fatal: could not open pty\n"); + exit(1); + } + + tcgetattr(master, &term); + cfmakeraw(&term); + tcsetattr(master, 0, &term); + + tcgetattr(slave, &term); + cfmakeraw(&term); + tcsetattr(slave, 0, &term); + + fout = fdopen(master, "w"); + fin = fdopen(master, "r"); + + if (fout == NULL || fin == NULL) { + fprintf(stderr, "fatal: could not reopen pty\n"); + exit(1); + } + + qemu_set_fd_handler2(slave, NULL, pty_read_proxy, NULL, chr); + qemu_chr_add_handlers(chr, chr_can_read_proxy, chr_read_proxy, + chr_event_proxy, chr); + + screen = newterm(getenv("TERM"), fout, fin); + set_term(screen); + + /* we won't get resizes so we need to start out telling curses the + proper size */ + resize_term(25, 80); + } else +#endif + initscr(); + /* input as raw as possible, let everything be interpreted * by the guest system */ - initscr(); noecho(); intrflush(stdscr, FALSE); + noecho(); intrflush(stdscr, FALSE); nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); start_color(); raw(); scrollok(stdscr, FALSE); @@ -332,7 +420,7 @@ static void curses_keyboard_setup(void) } } -void curses_display_init(DisplayState *ds, int full_screen) +void curses_display_init(DisplayState *ds, CharDriverState *chr, int full_screen) { #ifndef _WIN32 if (!isatty(1)) { @@ -341,7 +429,7 @@ void curses_display_init(DisplayState *ds, int full_screen) } #endif - curses_setup(); + curses_setup(chr); curses_keyboard_setup(); atexit(curses_atexit); diff --git a/vl.c b/vl.c index d5a1dbc..16c9900 100644 --- a/vl.c +++ b/vl.c @@ -172,7 +172,6 @@ BlockDriverState *bs_snapshots; int vga_ram_size; static DisplayState display_state; int nographic; -int curses; const char* keyboard_layout = NULL; int64_t ticks_per_sec; int ram_size; @@ -7691,7 +7690,7 @@ static void help(int exitcode) "-no-acpi disable ACPI\n" #endif #ifdef CONFIG_CURSES - "-curses use a curses/ncurses interface instead of SDL\n" + "-curses dev use a curses/ncurses to char driver 'dev' instead of SDL\n" #endif "-no-reboot exit instead of rebooting\n" "-loadvm file start right away with a saved state (loadvm in monitor)\n" @@ -7896,7 +7895,7 @@ const QEMUOption qemu_options[] = { { "smp", HAS_ARG, QEMU_OPTION_smp }, { "vnc", HAS_ARG, QEMU_OPTION_vnc }, #ifdef CONFIG_CURSES - { "curses", 0, QEMU_OPTION_curses }, + { "curses", HAS_ARG, QEMU_OPTION_curses }, #endif /* temporary options */ @@ -8190,6 +8189,7 @@ int main(int argc, char **argv) int fds[2]; const char *pid_file = NULL; VLANState *vlan; + const char *curses_device = NULL; LIST_INIT (&vm_change_state_head); #ifndef _WIN32 @@ -8234,7 +8234,6 @@ int main(int argc, char **argv) #endif snapshot = 0; nographic = 0; - curses = 0; kernel_filename = NULL; kernel_cmdline = ""; cyls = heads = secs = 0; @@ -8411,7 +8410,7 @@ int main(int argc, char **argv) break; #ifdef CONFIG_CURSES case QEMU_OPTION_curses: - curses = 1; + curses_device = optarg; break; #endif case QEMU_OPTION_portrait: @@ -8956,7 +8955,7 @@ int main(int argc, char **argv) /* terminal init */ memset(&display_state, 0, sizeof(display_state)); if (nographic) { - if (curses) { + if (curses_device) { fprintf(stderr, "fatal: -nographic can't be used with -curses\n"); exit(1); } @@ -8968,8 +8967,19 @@ int main(int argc, char **argv) exit(1); } else #if defined(CONFIG_CURSES) - if (curses) { - curses_display_init(ds, full_screen); + if (curses_device) { + CharDriverState *chr = NULL; + +#ifndef _WIN32 + if (strcmp(curses_device, "stdio") != 0) { + chr = qemu_chr_open(curses_device); + if (chr == NULL) { + fprintf(stderr, "fatal: could not open curses device\n"); + exit(1); + } + } +#endif + curses_display_init(ds, chr, full_screen); } else #endif {