#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qemu-gtk.h" #include "tcp.h" #define APPNAME "qemu-gtk" #ifdef VTE_CHECK_VERSION # if VTE_CHECK_VERSION(0, 16, 15) # define HAVE_BLINK_MODE 1 # endif #endif /* ------------------------------------------------------------------ */ static GKeyFile *config; static char config_file[1024]; static int config_dirty; static void config_read(void) { char *home = getenv("HOME"); GError *err = NULL; if (!home) return; snprintf(config_file, sizeof(config_file), "%s/.qemu-gtk.rc", home); config = g_key_file_new(); g_key_file_load_from_file(config, config_file, G_KEY_FILE_KEEP_COMMENTS, &err); } static void config_write(void) { char *data; gsize len; GError *err = NULL; int fd; if (!config_dirty) return; data = g_key_file_to_data(config, &len, &err); fd = open(config_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (-1 == fd) return; write(fd, data, len); fsync(fd); close(fd); } /* ------------------------------------------------------------------ */ static void vte_set_blink(GtkWidget *vte, gboolean blink) { #ifdef HAVE_BLINK_MODE VteTerminalCursorBlinkMode bl = blink ? VTE_CURSOR_BLINK_ON : VTE_CURSOR_BLINK_OFF; vte_terminal_set_cursor_blink_mode(VTE_TERMINAL(vte), bl); #else vte_terminal_set_cursor_blinks(VTE_TERMINAL(vte), blink); #endif } static void vtes_set_font(struct qemu_window *win) { struct qemu_conn *term; if (win->monitor.vte) vte_terminal_set_font_from_string(VTE_TERMINAL(win->monitor.vte), win->tty_font); if (win->gdb_vte) vte_terminal_set_font_from_string(VTE_TERMINAL(win->gdb_vte), win->tty_font); for (term = win->terms; term != NULL; term = term->next) vte_terminal_set_font_from_string(VTE_TERMINAL(term->vte), win->tty_font); } static void vtes_set_colors(struct qemu_window *win) { struct qemu_conn *term; GdkColor fg = {0,0,0,0}; GdkColor bg = {0,0,0,0}; gdk_color_parse(win->tty_fg, &fg); gdk_color_parse(win->tty_bg, &bg); if (win->monitor.vte) { vte_terminal_set_color_foreground(VTE_TERMINAL(win->monitor.vte), &fg); vte_terminal_set_color_background(VTE_TERMINAL(win->monitor.vte), &bg); } if (win->gdb_vte) { vte_terminal_set_color_foreground(VTE_TERMINAL(win->gdb_vte), &fg); vte_terminal_set_color_background(VTE_TERMINAL(win->gdb_vte), &bg); } for (term = win->terms; term != NULL; term = term->next) { vte_terminal_set_color_foreground(VTE_TERMINAL(term->vte), &fg); vte_terminal_set_color_background(VTE_TERMINAL(term->vte), &bg); } } static void vtes_set_blink(struct qemu_window *win) { struct qemu_conn *term; if (win->monitor.vte) vte_set_blink(win->monitor.vte, win->tty_blink); if (win->gdb_vte) vte_set_blink(win->gdb_vte, win->tty_blink); for (term = win->terms; term != NULL; term = term->next) vte_set_blink(term->vte, win->tty_blink); } static void vte_configure(struct qemu_window *win, GtkWidget *vte) { GdkColor fg = {0,0,0,0}; GdkColor bg = {0,0,0,0}; vte_terminal_set_scrollback_lines(VTE_TERMINAL(vte), 4096); vte_terminal_set_backspace_binding(VTE_TERMINAL(vte), VTE_ERASE_ASCII_BACKSPACE); gdk_color_parse(win->tty_fg, &fg); gdk_color_parse(win->tty_bg, &bg); vte_terminal_set_color_foreground(VTE_TERMINAL(vte), &fg); vte_terminal_set_color_background(VTE_TERMINAL(vte), &bg); vte_terminal_set_font_from_string(VTE_TERMINAL(vte), win->tty_font); vte_set_blink(vte, win->tty_blink); } static void tabs_configure(struct qemu_window *win) { gboolean showtabs = 1; if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->tab)) == 1) showtabs = 0; if (win->fullscreen) showtabs = 0; if (showtabs) { gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->tab), 1); gtk_notebook_set_show_border(GTK_NOTEBOOK(win->tab), 1); } else { gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->tab), 0); gtk_notebook_set_show_border(GTK_NOTEBOOK(win->tab), 0); } } static void tabs_add(struct qemu_window *win, GtkWidget *child, const char *text, int pos) { GtkWidget *label; label = gtk_label_new(text); gtk_notebook_insert_page(GTK_NOTEBOOK(win->tab), child, label, pos); gtk_widget_show(child); if (0 == pos) { gtk_notebook_set_current_page(GTK_NOTEBOOK(win->tab), 0); gtk_widget_grab_focus(child); } tabs_configure(win); } /* ------------------------------------------------------------------ */ static void menu_cb_fullscreen(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; gboolean active; active = gtk_toggle_action_get_active(action); if (active) { gtk_window_fullscreen(GTK_WINDOW(win->toplevel)); } else { gtk_window_unfullscreen(GTK_WINDOW(win->toplevel)); } } static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, gpointer userdata) { struct qemu_window *win = userdata; GtkWidget *item; if (!(event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)) return TRUE; win->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; item = gtk_ui_manager_get_widget(win->ui, "/MainMenu/ViewMenu/FullScreen"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), win->fullscreen); tabs_configure(win); if (win->fullscreen) { gtk_widget_hide(win->fstatus); } else { gtk_widget_show(win->fstatus); } return TRUE; } /* ------------------------------------------------------------------ */ static void menu_cb_scale_display(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; win->vnc_scale = gtk_toggle_action_get_active(action); if (win->vnc) vnc_display_set_scaling(VNC_DISPLAY(win->vnc), win->vnc_scale); g_key_file_set_boolean(config, "vnc", "scale", win->vnc_scale); config_dirty++; } static void menu_cb_blink_cursor(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; win->tty_blink = gtk_toggle_action_get_active(action); vtes_set_blink(win); g_key_file_set_boolean(config, "tty", "blink", win->tty_blink); config_dirty++; } static void menu_cb_grab_mouse(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; win->vnc_grab_mouse = gtk_toggle_action_get_active(action); if (win->vnc) vnc_display_set_pointer_grab(VNC_DISPLAY(win->vnc), win->vnc_grab_mouse); g_key_file_set_boolean(config, "vnc", "grab-mouse", win->vnc_grab_mouse); config_dirty++; } static void menu_cb_grab_kbd(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; win->vnc_grab_kbd = gtk_toggle_action_get_active(action); if (win->vnc) vnc_display_set_keyboard_grab(VNC_DISPLAY(win->vnc), win->vnc_grab_kbd); g_key_file_set_boolean(config, "vnc", "grab-kbd", win->vnc_grab_kbd); config_dirty++; } static void menu_cb_config_font(GtkAction *action, void *data) { struct qemu_window *win = data; GtkWidget *dialog; dialog = gtk_font_selection_dialog_new("Terminal font"); if (win->tty_font) gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG(dialog), win->tty_font); gtk_widget_show_all(dialog); switch (gtk_dialog_run(GTK_DIALOG(dialog))) { case GTK_RESPONSE_OK: win->tty_font = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG(dialog)); g_key_file_set_string(config, "tty", "font", win->tty_font); config_dirty++; vtes_set_font(win); break; } gtk_widget_destroy(dialog); } static int pickcolor(char *title, char *group, char *key, char *current) { GtkWidget *dialog; GdkColor color = {0,0,0,0}; GtkColorSelection *csel; char name[16]; int rc = -1; gdk_color_parse(current, &color); dialog = gtk_color_selection_dialog_new(title); csel = GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel); gtk_color_selection_set_has_opacity_control(csel, FALSE); gtk_color_selection_set_current_color(csel, &color); gtk_widget_show_all(dialog); switch (gtk_dialog_run(GTK_DIALOG(dialog))) { case GTK_RESPONSE_OK: gtk_color_selection_get_current_color(csel, &color); snprintf(name, sizeof(name), "#%04x%04x%04x", color.red, color.green, color.blue); g_key_file_set_string(config, group, key, name); config_dirty++; rc = 0; } gtk_widget_destroy(dialog); return rc; } static void menu_cb_config_fg(GtkAction *action, void *data) { struct qemu_window *win = data; GError *err = NULL; if (0 != pickcolor("Terminal text color", "tty", "foreground", win->tty_fg)) return; win->tty_fg = g_key_file_get_string(config, "tty", "foreground", &err); vtes_set_colors(win); } static void menu_cb_config_bg(GtkAction *action, void *data) { struct qemu_window *win = data; GError *err = NULL; if (0 != pickcolor("Terminal background", "tty", "background", win->tty_bg)) return; win->tty_bg = g_key_file_get_string(config, "tty", "background", &err); vtes_set_colors(win); } static void menu_cb_send_ctrlaltdel(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_Delete }; vnc_display_send_keys_ex(VNC_DISPLAY(win->vnc), keys, sizeof (keys) / sizeof (keys[0]), VNC_DISPLAY_KEY_EVENT_CLICK); } static void menu_cb_devices_rescan(GtkToggleAction *action, gpointer userdata) { struct qemu_window *win = userdata; devices_rescan(win); } static void menu_cb_about(GtkAction *action, gpointer userdata) { static char *comments = "gtk ui for qemu"; static char *copyright = "(c) 2008 Gerd Hoffmann"; static char *authors[] = { "Gerd Hoffmann ", NULL }; struct qemu_window *win = userdata; gtk_show_about_dialog(GTK_WINDOW(win->toplevel), "authors", authors, "comments", comments, "copyright", copyright, "logo-icon-name", GTK_STOCK_ABOUT, "version", VERSION, NULL); } static void menu_cb_close(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; gtk_widget_destroy(win->toplevel); } static void menu_cb_monitor_stop(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; monitor_append(win, "stop"); } static void menu_cb_monitor_cont(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; monitor_append(win, "cont"); } static void menu_cb_monitor_sys_reset(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; monitor_append(win, "system_reset"); } static void menu_cb_monitor_sys_powerdown(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; monitor_append(win, "system_powerdown"); } static void menu_cb_monitor_quit(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; monitor_append(win, "quit"); } static void menu_cb_run_gdb(GtkAction *action, gpointer userdata) { struct qemu_window *win = userdata; char *argv[] = { "gdb", NULL }; char cmd[256]; int len; if (win->gdb_vte) return; win->gdb_vte = vte_terminal_new(); tabs_add(win, win->gdb_vte, "gdb", -1); vte_configure(win, win->gdb_vte); monitor_append(win, "gdbserver 1234"); len = snprintf(cmd, sizeof(cmd), "target remote %s:1234\n", win->monitor.hostname); win->gdb_pid = vte_terminal_fork_command(VTE_TERMINAL(win->gdb_vte), argv[0], argv, NULL, NULL, FALSE, FALSE, FALSE); vte_terminal_feed_child(VTE_TERMINAL(win->gdb_vte), cmd, len); } void update_status(struct qemu_window *win) { char st[256]; char ti[256]; int tl, sl = 0; tl = snprintf(ti, sizeof(ti), "%s", APPNAME); if (win->monitor.handle == -1) { sl = snprintf(st, sizeof(st), "Not connected."); goto out; } if (win->vnc_state == VNC_INITIALIZED) { sl += snprintf(st+sl, sizeof(st)-sl, "VNC display \"%s\", %dx%d", win->vnc_display, win->vnc_width, win->vnc_height); } else { sl += snprintf(st+sl, sizeof(st)-sl, "No VNC"); } if (win->version && strlen(win->version)) sl += snprintf(st+sl, sizeof(st)-sl, ", qemu %s", win->version); if (win->name && strlen(win->name)) { tl = snprintf(ti, sizeof(ti), "%s (%s)", win->name, APPNAME); sl += snprintf(st+sl, sizeof(st)-sl, ", name \"%s\"", win->name); } sl += snprintf(st+sl, sizeof(st)-sl, "."); if (win->vnc_grab) { sl = snprintf(st, sizeof(st), "Press Ctrl-Alt to release input grab."); goto out; } out: gtk_window_set_title(GTK_WINDOW(win->toplevel), ti); gtk_label_set_text(GTK_LABEL(win->status), st); } /* ------------------------------------------------------------------ */ static void vnc_connected(GtkWidget *vncdisplay, void *data) { struct qemu_window *win = data; win->vnc_state = VNC_CONNECTED; update_status(win); } static void vnc_initialized(GtkWidget *vncdisplay, void *data) { struct qemu_window *win = data; win->vnc_state = VNC_INITIALIZED; update_status(win); } static void vnc_disconnected(GtkWidget *vncdisplay, void *data) { struct qemu_window *win = data; win->vnc_state = VNC_DISCONNECTED; update_status(win); } static void vnc_grab(GtkWidget *vncdisplay, void *data) { struct qemu_window *win = data; win->vnc_grab = 1; update_status(win); } static void vnc_ungrab(GtkWidget *vncdisplay, void *data) { struct qemu_window *win = data; win->vnc_grab = 0; update_status(win); } static void vnc_desktop_resize(GtkWidget *vncdisplay, int x, int y, void *data) { struct qemu_window *win = data; win->vnc_width = x; win->vnc_height = y; update_status(win); } static void vnc_credential(GtkWidget *vncdisplay, GValueArray *credList, void *data) { struct qemu_window *win = data; char *val; int i; for (i = 0 ; i < credList->n_values ; i++) { GValue *cred = g_value_array_get_nth(credList, i); switch (g_value_get_enum(cred)) { case VNC_DISPLAY_CREDENTIAL_PASSWORD: val = win->vnc_password; break; default: fprintf(stderr, "can't handle credential type %d\n", g_value_get_enum(cred)); return; } vnc_display_set_credential(VNC_DISPLAY(win->vnc), g_value_get_enum(cred), val); } } void vnc_connect(struct qemu_window *win) { int nr; if (2 == sscanf(win->vnc_display, "%127[^:]:%d", win->vnc_hostname, &nr)) { sprintf(win->vnc_tcpport, "%d", nr + 5900); } else if (1 == sscanf(win->vnc_display, ":%d", &nr)) { sprintf(win->vnc_hostname, "%s", win->monitor.hostname); sprintf(win->vnc_tcpport, "%d", nr + 5900); } else { fprintf(stderr, "parse error: \"%s\"\n", win->vnc_display); return; } qemu_vnc_tab(win); vnc_display_open_host(VNC_DISPLAY(win->vnc), win->vnc_hostname, win->vnc_tcpport); } /* ------------------------------------------------------------------ */ int conn_init(struct qemu_conn *conn, char *name, char *dest) { struct addrinfo ask; char path[256]; char serv[33]; memset(&ask,0,sizeof(ask)); ask.ai_socktype = SOCK_STREAM; ask.ai_family = PF_UNSPEC; tcp_verbose = 1; snprintf(conn->name, sizeof(conn->name), "%s", name); strcpy(conn->hostname, "localhost"); if (2 == sscanf(dest, "tcp:%64[^:]:%32s", conn->hostname, serv)) { conn->handle = tcp_connect(&ask, NULL, NULL, conn->hostname, serv); } else if (1 == sscanf(dest, "tcp:%32s", serv)) { conn->handle = tcp_connect(&ask, NULL, NULL, conn->hostname, serv); } else if (1 == sscanf(dest, "unix:%255s", path)) { conn->handle = unix_connect(path); } else if (1 == sscanf(dest, "pipe:%255s", path)) { conn->handle = pipe_connect(path); } else if (1 == sscanf(dest, "pty:%255s", path)) { conn->handle = cdev_connect(path); } else if (1 == sscanf(dest, "pts:%255s", path)) { conn->handle = cdev_connect(path); } else { fprintf(stderr, "can't parse \"%s\"\n", dest); conn->handle = -1; } return conn->handle; } static void conn_user_input(VteTerminal *vte, gchar *buf, guint len, gpointer data) { struct qemu_conn *conn = data; if (conn->handle != -1) write(conn->handle, buf, len); } static gboolean conn_watch(GIOChannel *source, GIOCondition condition, gpointer data) { struct qemu_conn *conn = data; char buf[256]; int rc; rc = read(conn->handle, buf, sizeof(buf)); switch(rc) { case -1: if (EINTR == errno) break; perror("console: read"); goto close; case 0: fprintf(stderr, "console: EOF\n"); goto close; default: if (conn->vte) vte_terminal_feed(VTE_TERMINAL(conn->vte), buf, rc); break; } return TRUE; close: if (conn->vte) vte_terminal_feed(VTE_TERMINAL(conn->vte), "\r\n=== CLOSED ===", 16); close(conn->handle); conn->handle = -1; return FALSE; } int conn_open_term(struct qemu_window *win, char *name, char *dest, int pos) { struct qemu_conn *conn; int fd; for (conn = win->terms; conn != NULL; conn = conn->next) { if (strcmp(conn->name, name) == 0) { /* TODO: maybe bring tab to front? */ return 0; } } conn = malloc(sizeof(*conn)); if (!conn) return -1; memset(conn,0,sizeof(*conn)); fd = conn_init(conn, name, dest); if (fd == -1) { free(conn); return -1; } conn->next = win->terms; win->terms = conn; conn->ch = g_io_channel_unix_new(fd); conn->id = g_io_add_watch(conn->ch, G_IO_IN, conn_watch, conn); qemu_conn_tab(win, conn, pos); return 0; } /* ------------------------------------------------------------------ */ static const GtkActionEntry entries[] = { { /* --- menu bar --- */ .name = "FileMenu", .label = "_File", },{ .name = "ViewMenu", .label = "_View", },{ .name = "InputMenu", .label = "_Input", },{ .name = "VMMenu", .label = "_VM", },{ .name = "DevicesMenu", .label = "_Devices", },{ .name = "HelpMenu", .label = "_Help", },{ /* --- sub menus --- */ .name = "ChangeMediaSubMenu", .label = "_Change media", },{ .name = "EjectMediaSubMenu", .label = "_Eject media", },{ .name = "PlugUsbSubMenu", .label = "_Plugin USB", },{ .name = "UnplugUsbSubMenu", .label = "_Unplug USB", },{ .name = "OpenTermSubMenu", .label = "_Open Terminal", },{ /* --- file menu --- */ .name = "Close", .stock_id = GTK_STOCK_CLOSE, .label = "_Close", .accelerator = "Q", .callback = G_CALLBACK(menu_cb_close), },{ /* --- view menu --- */ .name = "TerminalFont", .stock_id = GTK_STOCK_SELECT_FONT, .label = "Terminal _font ...", .callback = G_CALLBACK(menu_cb_config_font), },{ .name = "TerminalForeground", .stock_id = GTK_STOCK_SELECT_COLOR, .label = "Terminal _text color ...", .callback = G_CALLBACK(menu_cb_config_fg), },{ .name = "TerminalBackground", .label = "Terminal _background ...", .callback = G_CALLBACK(menu_cb_config_bg), },{ /* --- input menu --- */ .name = "SendCtrlAltDel", .label = "Send Ctrl-Alt-Del", .callback = G_CALLBACK(menu_cb_send_ctrlaltdel), },{ /* --- vm menu --- */ .name = "MonitorStop", .stock_id = GTK_STOCK_MEDIA_PAUSE, .label = "_Pause", .callback = G_CALLBACK(menu_cb_monitor_stop), },{ .name = "MonitorCont", .stock_id = GTK_STOCK_MEDIA_PLAY, .label = "_Unpause", .callback = G_CALLBACK(menu_cb_monitor_cont), },{ .name = "MonitorSysReset", .label = "_Reset", .callback = G_CALLBACK(menu_cb_monitor_sys_reset), },{ .name = "MonitorSysPowerdown", .label = "_Shutdown", .callback = G_CALLBACK(menu_cb_monitor_sys_powerdown), },{ .name = "MonitorQuit", .label = "_Destroy", .callback = G_CALLBACK(menu_cb_monitor_quit), },{ .name = "RunGdb", .label = "_Debug with gdb", .callback = G_CALLBACK(menu_cb_run_gdb), },{ /* --- devices menu --- */ .name = "DevicesRescan", .label = "_Rescan devices", .callback = G_CALLBACK(menu_cb_devices_rescan), },{ /* --- help menu --- */ .name = "About", .stock_id = GTK_STOCK_ABOUT, .label = "_About ...", .callback = G_CALLBACK(menu_cb_about), }, }; static const GtkToggleActionEntry tentries[] = { { .name = "ScaleDisplay", .label = "_Scale VNC display", .callback = G_CALLBACK(menu_cb_scale_display), },{ .name = "FullScreen", .stock_id = GTK_STOCK_FULLSCREEN, .label = "_Fullscreen", .accelerator = "F11", .callback = G_CALLBACK(menu_cb_fullscreen), },{ .name = "GrabMouse", .label = "Enable _mouse grab", .callback = G_CALLBACK(menu_cb_grab_mouse), },{ .name = "GrabKbd", .label = "Enable _keyboard grab", .callback = G_CALLBACK(menu_cb_grab_kbd), },{ .name = "TerminalBlink", .label = "Blinking cursor", .callback = G_CALLBACK(menu_cb_blink_cursor), } }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef WITH_TOOLBAR " " " " " " #endif ""; static void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } static struct qemu_window *qemu_create_window(void) { struct qemu_window *win; GtkWidget *vbox, *menubar, *toolbar, *item; GtkAccelGroup *accel; GtkActionGroup *ag; GError *err; win = malloc(sizeof(*win)); if (NULL == win) return NULL; memset(win,0,sizeof(*win)); win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(win->toplevel), APPNAME); gtk_window_set_default_size(GTK_WINDOW(win->toplevel), 320, 280); g_signal_connect(G_OBJECT(win->toplevel), "destroy", G_CALLBACK(destroy), win); g_signal_connect(G_OBJECT(win->toplevel), "window-state-event", G_CALLBACK(window_state_cb), win); /* menu + toolbar */ win->ui = gtk_ui_manager_new(); ag = gtk_action_group_new("MenuActions"); gtk_action_group_add_actions(ag, entries, G_N_ELEMENTS(entries), win); gtk_action_group_add_toggle_actions(ag, tentries, G_N_ELEMENTS(tentries), win); gtk_ui_manager_insert_action_group(win->ui, ag, 0); accel = gtk_ui_manager_get_accel_group(win->ui); gtk_window_add_accel_group(GTK_WINDOW(win->toplevel), accel); err = NULL; if (!gtk_ui_manager_add_ui_from_string(win->ui, ui_xml, -1, &err)) { g_message("building menus failed: %s", err->message); g_error_free(err); exit(1); } /* main area */ win->tab = gtk_notebook_new(); /* status line */ win->status = gtk_label_new("status line"); gtk_misc_set_alignment(GTK_MISC(win->status), 0, 0.5); gtk_misc_set_padding(GTK_MISC(win->status), 3, 1); update_status(win); /* Make a vbox and put stuff in */ vbox = gtk_vbox_new(FALSE, 1); gtk_container_set_border_width(GTK_CONTAINER(vbox), 1); gtk_container_add(GTK_CONTAINER(win->toplevel), vbox); menubar = gtk_ui_manager_get_widget(win->ui, "/MainMenu"); gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); toolbar = gtk_ui_manager_get_widget(win->ui, "/ToolBar"); if (toolbar) gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), win->tab, TRUE, TRUE, 0); win->fstatus = gtk_frame_new(NULL); gtk_box_pack_end(GTK_BOX(vbox), win->fstatus, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(win->fstatus), win->status); /* read config */ err = NULL; win->vnc_scale = g_key_file_get_boolean(config, "vnc", "scale", &err); err = NULL; win->vnc_grab_mouse = g_key_file_get_boolean(config, "vnc", "grab-mouse", &err); err = NULL; win->vnc_grab_kbd = g_key_file_get_boolean(config, "vnc", "grab-kbd", &err); err = NULL; win->tty_font = g_key_file_get_string(config, "tty", "font", &err); err = NULL; win->tty_fg = g_key_file_get_string(config, "tty", "foreground", &err); err = NULL; win->tty_bg = g_key_file_get_string(config, "tty", "background", &err); err = NULL; win->tty_blink = g_key_file_get_boolean(config, "tty", "blink", &err); /* config defaults */ if (!win->tty_font) win->tty_font = "console 12"; if (!win->tty_fg) win->tty_fg = "white"; if (!win->tty_bg) win->tty_bg = "black"; /* apply config */ item = gtk_ui_manager_get_widget(win->ui, "/MainMenu/ViewMenu/ScaleDisplay"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), win->vnc_scale); item = gtk_ui_manager_get_widget(win->ui, "/MainMenu/ViewMenu/TerminalBlink"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), win->tty_blink); item = gtk_ui_manager_get_widget(win->ui, "/MainMenu/InputMenu/GrabMouse"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), win->vnc_grab_mouse); item = gtk_ui_manager_get_widget(win->ui, "/MainMenu/InputMenu/GrabKbd"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), win->vnc_grab_kbd); return win; } void qemu_vnc_tab(struct qemu_window *win) { if (win->vnc) return; win->vnc = vnc_display_new(); vnc_display_set_scaling(VNC_DISPLAY(win->vnc), win->vnc_scale); vnc_display_set_pointer_grab(VNC_DISPLAY(win->vnc), win->vnc_grab_mouse); vnc_display_set_keyboard_grab(VNC_DISPLAY(win->vnc), win->vnc_grab_kbd); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-connected", GTK_SIGNAL_FUNC(vnc_connected), win); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-initialized", GTK_SIGNAL_FUNC(vnc_initialized), win); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-disconnected", GTK_SIGNAL_FUNC(vnc_disconnected), win); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-pointer-grab", GTK_SIGNAL_FUNC(vnc_grab), win); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-pointer-ungrab", GTK_SIGNAL_FUNC(vnc_ungrab), win); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-auth-credential", GTK_SIGNAL_FUNC(vnc_credential), win); gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-desktop-resize", GTK_SIGNAL_FUNC(vnc_desktop_resize), win); tabs_add(win, win->vnc, "vnc display", 0); } void qemu_conn_tab(struct qemu_window *win, struct qemu_conn *conn, int pos) { if (conn->vte) return; conn->vte = vte_terminal_new(); g_signal_connect(conn->vte, "commit", G_CALLBACK(conn_user_input), conn); tabs_add(win, conn->vte, conn->name, pos); vte_configure(win, conn->vte); } /* ------------------------------------------------------------------ */ int debug = 0; static void usage(FILE *fp) { fprintf(fp, "This is a simple qemu gui\n" "\n" "usage: %s [ options ] monitor\n" "options:\n" " -h Print this text.\n" " -d Raise debug level.\n" " -k Keep window open on VM shutdown.\n" " -m Enable monitor logging and access.\n" " -s Enable serial console.\n" " -t Disable VNC, enable serial console.\n" "\n" "-- \n" "(c) 2008 Gerd Hoffmann \n", APPNAME); } static void vm_lookup(char *mon, int len, char *arg) { GError *err = NULL; char *ptr; /* monitor connection specified? */ if (strchr(arg,':') || strchr(arg,'/')) { snprintf(mon, len, "%s", arg); goto done; } /* listed in config file? */ ptr = g_key_file_get_string(config, "vm", arg, &err); if (ptr) { snprintf(mon, len, "%s", ptr); goto done; } /* try socket created by qemu-run script */ snprintf(mon, len, "unix:%s/.qemu-gtk/%s", getenv("HOME"), arg); done: if (debug) fprintf(stderr, "monitor: %s\n", mon); } int main(int argc, char *argv[]) { struct qemu_window *win; char mon[256]; int monitor_tab = 0; int sercon_tab = 0; int vnc_tab = 1; int quit_on_shutdown = 1; int c; gtk_init(&argc, &argv); for (;;) { if (-1 == (c = getopt(argc, argv, "hdkmst"))) break; switch (c) { case 'd': debug++; break; case 'k': quit_on_shutdown = 0; break; case 'm': monitor_tab = 1; break; case 's': sercon_tab = 1; break; case 't': vnc_tab = 0; sercon_tab = 1; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } if (optind == argc) { usage(stderr); exit(1); } /* init */ config_read(); /* main window */ win = qemu_create_window(); vm_lookup(mon, sizeof(mon), argv[optind]); if (-1 == monitor_connect(win, mon)) { exit(1); } win->quit_on_shutdown = quit_on_shutdown; win->sercon_tab = sercon_tab; win->vnc_tab = vnc_tab; gtk_widget_show_all(win->toplevel); /* tabs */ if (monitor_tab) qemu_conn_tab(win, &win->monitor, -1); /* main loop */ gtk_main(); /* cleanup */ config_write(); if (win->gdb_pid) kill(win->gdb_pid, SIGTERM); exit(0); }