From patchwork Wed Feb 20 13:43:21 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 222087 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8014D2C007C for ; Thu, 21 Feb 2013 00:44:13 +1100 (EST) Received: from localhost ([::1]:58072 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U89xz-00007K-3c for incoming@patchwork.ozlabs.org; Wed, 20 Feb 2013 08:44:11 -0500 Received: from eggs.gnu.org ([208.118.235.92]:45112) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U89xd-0008VE-0s for qemu-devel@nongnu.org; Wed, 20 Feb 2013 08:43:53 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U89xa-0005Sa-CI for qemu-devel@nongnu.org; Wed, 20 Feb 2013 08:43:48 -0500 Received: from e28smtp01.in.ibm.com ([122.248.162.1]:41903) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U89xZ-0005S9-Hh for qemu-devel@nongnu.org; Wed, 20 Feb 2013 08:43:46 -0500 Received: from /spool/local by e28smtp01.in.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 20 Feb 2013 19:10:55 +0530 Received: from d28dlp02.in.ibm.com (9.184.220.127) by e28smtp01.in.ibm.com (192.168.1.131) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 20 Feb 2013 19:10:53 +0530 Received: from d28relay01.in.ibm.com (d28relay01.in.ibm.com [9.184.220.58]) by d28dlp02.in.ibm.com (Postfix) with ESMTP id 1EBD43940057 for ; Wed, 20 Feb 2013 19:13:40 +0530 (IST) Received: from d28av04.in.ibm.com (d28av04.in.ibm.com [9.184.220.66]) by d28relay01.in.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r1KDhadI17432764 for ; Wed, 20 Feb 2013 19:13:36 +0530 Received: from d28av04.in.ibm.com (loopback [127.0.0.1]) by d28av04.in.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id r1KDhdqq014102 for ; Thu, 21 Feb 2013 00:43:39 +1100 Received: from titi.austin.rr.com (sig-9-76-138-226.mts.ibm.com [9.76.138.226]) by d28av04.in.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id r1KDhSxv013442; Thu, 21 Feb 2013 00:43:38 +1100 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Wed, 20 Feb 2013 07:43:21 -0600 Message-Id: <1361367806-4599-5-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.8.0 In-Reply-To: <1361367806-4599-1-git-send-email-aliguori@us.ibm.com> References: <1361367806-4599-1-git-send-email-aliguori@us.ibm.com> X-Content-Scanned: Fidelis XPS MAILER x-cbid: 13022013-4790-0000-0000-000007005EF4 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 122.248.162.1 Cc: Kevin Wolf , Anthony Liguori Subject: [Qemu-devel] [PATCH 4/9] gtk: add virtual console support (v2) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This enables VteTerminal to be used to render the text consoles. VteTerminal is the same widget used by gnome-terminal which means it's VT100 emulation is as good as they come. It's also screen reader accessible, supports copy/paste, proper scrolling and most of the other features you would expect from a terminal widget. Signed-off-by: Anthony Liguori --- v1 -> v2 - make sure to activate the menu item when switching tabs - fix sizing of non-0 pages - fix build with newer gcc --- ui/gtk.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index 85e7999..94f0461 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -47,6 +47,7 @@ #include "qmp-commands.h" #include "x_keymap.h" #include "keymaps.h" +#include "char/char.h" //#define DEBUG_GTK @@ -56,6 +57,8 @@ #define DPRINTF(fmt, ...) do { } while (0) #endif +#define MAX_VCS 10 + typedef struct VirtualConsole { GtkWidget *menu_item; @@ -79,6 +82,9 @@ typedef struct GtkDisplayState GtkWidget *view_menu; GtkWidget *vga_item; + int nb_vcs; + VirtualConsole vc[MAX_VCS]; + GtkWidget *show_tabs_item; GtkWidget *vbox; @@ -403,6 +409,15 @@ static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0); + } else { + int i; + + for (i = 0; i < s->nb_vcs; i++) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1); + break; + } + } } } @@ -421,16 +436,153 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, gpointer data) { GtkDisplayState *s = data; + guint last_page; if (!gtk_widget_get_realized(s->notebook)) { return; } + last_page = gtk_notebook_get_current_page(nb); + + if (last_page) { + gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1); + } + + if (arg2 == 0) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); + } else { + VirtualConsole *vc = &s->vc[arg2 - 1]; + VteTerminal *term = VTE_TERMINAL(vc->terminal); + int width, height; + + width = 80 * vte_terminal_get_char_width(term); + height = 25 * vte_terminal_get_char_height(term); + + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); + gtk_widget_set_size_request(vc->terminal, width, height); + } + gd_update_cursor(s, TRUE); } +/** Virtual Console Callbacks **/ + +static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + VirtualConsole *vc = chr->opaque; + + return write(vc->fd, buf, len); +} + +static int nb_vcs; +static CharDriverState *vcs[MAX_VCS]; + +static CharDriverState *gd_vc_handler(QemuOpts *opts) +{ + CharDriverState *chr; + + chr = g_malloc0(sizeof(*chr)); + chr->chr_write = gd_vc_chr_write; + + vcs[nb_vcs++] = chr; + + return chr; +} + void early_gtk_display_init(void) { + register_vc_handler(gd_vc_handler); +} + +static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque) +{ + VirtualConsole *vc = opaque; + uint8_t buffer[1024]; + ssize_t len; + + len = read(vc->fd, buffer, sizeof(buffer)); + if (len <= 0) { + return FALSE; + } + + qemu_chr_be_write(vc->chr, buffer, len); + + return TRUE; +} + +static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group) +{ + const char *label; + char buffer[32]; + char path[32]; + VtePty *pty; + GIOChannel *chan; + GtkWidget *scrolled_window; + GtkAdjustment *vadjustment; + int master_fd, slave_fd, ret; + struct termios tty; + + snprintf(buffer, sizeof(buffer), "vc%d", index); + snprintf(path, sizeof(path), "/View/VC%d", index); + + vc->chr = vcs[index]; + + if (vc->chr->label) { + label = vc->chr->label; + } else { + label = buffer; + } + + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); + gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK); + + vc->terminal = vte_terminal_new(); + + ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL); + g_assert(ret != -1); + + /* Set raw attributes on the pty. */ + tcgetattr(slave_fd, &tty); + cfmakeraw(&tty); + tcsetattr(slave_fd, TCSAFLUSH, &tty); + + pty = vte_pty_new_foreign(master_fd, NULL); + + vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty); + + vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); + + vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); + + scrolled_window = gtk_scrolled_window_new(NULL, vadjustment); + gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal); + + vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); + + vc->fd = slave_fd; + vc->chr->opaque = vc; + vc->scrolled_window = scrolled_window; + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label)); + g_signal_connect(vc->menu_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + + gtk_menu_append(GTK_MENU(s->view_menu), vc->menu_item); + + qemu_chr_generic_open(vc->chr); + if (vc->chr->init) { + vc->chr->init(vc->chr); + } + + chan = g_io_channel_unix_new(vc->fd); + g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc); + + return group; } /** Window Creation **/ @@ -470,6 +622,7 @@ static void gd_create_menus(GtkDisplayState *s) GtkAccelGroup *accel_group; GSList *group = NULL; GtkWidget *separator; + int i; accel_group = gtk_accel_group_new(); s->file_menu = gtk_menu_new(); @@ -496,6 +649,13 @@ static void gd_create_menus(GtkDisplayState *s) gtk_accel_map_add_entry("/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK); gtk_menu_append(GTK_MENU(s->view_menu), s->vga_item); + for (i = 0; i < nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + + group = gd_vc_init(s, vc, i, group); + s->nb_vcs++; + } + separator = gtk_separator_menu_item_new(); gtk_menu_append(GTK_MENU(s->view_menu), separator);