diff mbox

[3/6] gtk: add virtual console support

Message ID 1329695104-15174-4-git-send-email-aliguori@us.ibm.com
State New
Headers show

Commit Message

Anthony Liguori Feb. 19, 2012, 11:45 p.m. UTC
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 <aliguori@us.ibm.com>
---
 ui/gtk.c |  138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 138 insertions(+), 0 deletions(-)

Comments

Stefan Weil Feb. 20, 2012, 9:13 p.m. UTC | #1
Am 20.02.2012 00:45, schrieb Anthony Liguori:
> 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 <aliguori@us.ibm.com>
> ---
> ui/gtk.c | 138 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 138 insertions(+), 0 deletions(-)
>


The new code uses VtePty and some functions which were added to
VteTerminal in 2010, so this is a rather new interface:

    commit dd08c50c6a6dd4349d3cbce271ddf4b741db8861
    Autor: Christian Persch <chpe@gnome.org> Do Jan 14 18:08:33 2010
    Eintragender: Christian Persch <chpe@gnome.org> Mi Mär 17 18:22:15 2010

    Add VtePty and adapt the VteTerminal APIs to it

    Add VtePty as a GObject holding the info about the PTY. Add new API to
    VteTerminal to set a VtePty, and deprecate the old API that takes a FD
    to the PTY. Also deprecate the whole of the undocumented _vte_pty_*()
    APIs.

    Add vte_terminal_fork_command_full() variant that allow providing a
    custom child setup function and that returns a GError on failure.


=> It does not work with Debian stable and other "old" distributions.

configure detects GTK+ and VTE, but make fails.

Regards,

Stefan Weil
Stefan Weil Feb. 25, 2012, 4:21 p.m. UTC | #2
Am 20.02.2012 00:45, schrieb Anthony Liguori:
> 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 <aliguori@us.ibm.com>
> ---
> ui/gtk.c | 138 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 138 insertions(+), 0 deletions(-)
>
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 502705b..bf65a4f 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
[...]
> +static int gd_vc_handler(QemuOpts *opts, CharDriverState **chrp)
> +{
> + CharDriverState *chr;
> +
> + chr = g_malloc0(sizeof(*chr));

Some time ago, there was a decision to prefer g_new / g_new0:

     chr = g_new0(CharDriverState, 1);

In function gtk_display_init there is also a g_malloc0 which
should be replaced by g_new0.

Regards,

Stefan Weil
Anthony Liguori Feb. 25, 2012, 7:49 p.m. UTC | #3
On 02/25/2012 10:21 AM, Stefan Weil wrote:
> Am 20.02.2012 00:45, schrieb Anthony Liguori:
>> 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 <aliguori@us.ibm.com>
>> ---
>> ui/gtk.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 files changed, 138 insertions(+), 0 deletions(-)
>>
>> diff --git a/ui/gtk.c b/ui/gtk.c
>> index 502705b..bf65a4f 100644
>> --- a/ui/gtk.c
>> +++ b/ui/gtk.c
> [...]
>> +static int gd_vc_handler(QemuOpts *opts, CharDriverState **chrp)
>> +{
>> + CharDriverState *chr;
>> +
>> + chr = g_malloc0(sizeof(*chr));
>
> Some time ago, there was a decision to prefer g_new / g_new0:

I'm not sure where the book of decisions is kept, but I certainly don't agree. 
a = malloc(sizeof(*a)) is an incredibly common pattern in QEMU.

It would be silly to change this pattern without good cause.

Regards,

Anthony Liguori

>
> chr = g_new0(CharDriverState, 1);
>
> In function gtk_display_init there is also a g_malloc0 which
> should be replaced by g_new0.
>
> Regards,
>
> Stefan Weil
>
>
Stefan Weil Feb. 25, 2012, 8:22 p.m. UTC | #4
Am 25.02.2012 20:49, schrieb Anthony Liguori:
> On 02/25/2012 10:21 AM, Stefan Weil wrote:
>> Am 20.02.2012 00:45, schrieb Anthony Liguori:
>>> 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 <aliguori@us.ibm.com>
>>> ---
>>> ui/gtk.c | 138 
>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>> 1 files changed, 138 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/ui/gtk.c b/ui/gtk.c
>>> index 502705b..bf65a4f 100644
>>> --- a/ui/gtk.c
>>> +++ b/ui/gtk.c
>> [...]
>>> +static int gd_vc_handler(QemuOpts *opts, CharDriverState **chrp)
>>> +{
>>> + CharDriverState *chr;
>>> +
>>> + chr = g_malloc0(sizeof(*chr));
>>
>> Some time ago, there was a decision to prefer g_new / g_new0:
>
> I'm not sure where the book of decisions is kept, but I certainly 
> don't agree. a = malloc(sizeof(*a)) is an incredibly common pattern in 
> QEMU.
>
> It would be silly to change this pattern without good cause.
>
> Regards,
>
> Anthony Liguori

Hi Anthony,

there is no book of decisions for QEMU, but there is best practice.
As far as I remember this topic was first discussed in this
qemu-devel thread:

http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg01988.html

a = malloc(sizeof(*a)) is no longer a valid pattern for QEMU
since you introduced glib-2.0. Those calls were converted
to a = g_malloc(sizeof(*a)) which was reasonable for a simple
automated code conversion.

I also used the g_malloc pattern in my patch, but was convinced
that glib-2.0 offers a better alternative using g_new.

Kind regards,

Stefan Weil
Anthony Liguori Feb. 25, 2012, 9:18 p.m. UTC | #5
On 02/25/2012 02:22 PM, Stefan Weil wrote:
> Am 25.02.2012 20:49, schrieb Anthony Liguori:
>> On 02/25/2012 10:21 AM, Stefan Weil wrote:
>>> Am 20.02.2012 00:45, schrieb Anthony Liguori:
>>>> 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 <aliguori@us.ibm.com>
>>>> ---
>>>> ui/gtk.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>> 1 files changed, 138 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/ui/gtk.c b/ui/gtk.c
>>>> index 502705b..bf65a4f 100644
>>>> --- a/ui/gtk.c
>>>> +++ b/ui/gtk.c
>>> [...]
>>>> +static int gd_vc_handler(QemuOpts *opts, CharDriverState **chrp)
>>>> +{
>>>> + CharDriverState *chr;
>>>> +
>>>> + chr = g_malloc0(sizeof(*chr));
>>>
>>> Some time ago, there was a decision to prefer g_new / g_new0:
>>
>> I'm not sure where the book of decisions is kept, but I certainly don't agree.
>> a = malloc(sizeof(*a)) is an incredibly common pattern in QEMU.
>>
>> It would be silly to change this pattern without good cause.
>>
>> Regards,
>>
>> Anthony Liguori
>
> Hi Anthony,
>
> there is no book of decisions for QEMU, but there is best practice.
> As far as I remember this topic was first discussed in this
> qemu-devel thread:
>
> http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg01988.html
>
> a = malloc(sizeof(*a)) is no longer a valid pattern for QEMU
> since you introduced glib-2.0. Those calls were converted
> to a = g_malloc(sizeof(*a)) which was reasonable for a simple
> automated code conversion.
>
> I also used the g_malloc pattern in my patch, but was convinced
> that glib-2.0 offers a better alternative using g_new.

I meant g_malloc of course.

If we want to have a "best practice", we should document it in CODING_STYLE. 
But I wouldn't agree with such a patch to coding style anyway.

Regards,

Anthony Liguori

>
> Kind regards,
>
> Stefan Weil
>
>
diff mbox

Patch

diff --git a/ui/gtk.c b/ui/gtk.c
index 502705b..bf65a4f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -35,6 +35,8 @@ 
 #define dprintf(fmt, ...) do { } while (0)
 #endif
 
+#define MAX_VCS 10
+
 typedef struct VirtualConsole
 {
     GtkWidget *menu_item;
@@ -58,6 +60,9 @@  typedef struct GtkDisplayState
     GtkWidget *view_menu;
     GtkWidget *vga_item;
 
+    int nb_vcs;
+    VirtualConsole vc[MAX_VCS];
+
     GtkWidget *show_tabs_item;
 
     GtkWidget *vbox;
@@ -379,6 +384,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;
+            }
+        }
     }
 }
 
@@ -405,8 +419,124 @@  static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
     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 int gd_vc_handler(QemuOpts *opts, CharDriverState **chrp)
+{
+    CharDriverState *chr;
+
+    chr = g_malloc0(sizeof(*chr));
+    chr->chr_write = gd_vc_chr_write;
+
+    *chrp = chr;
+
+    vcs[nb_vcs++] = chr;
+
+    return 0;
+}
+
 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 *adjustment;
+    int master_fd, slave_fd, ret;
+    struct termios tty;
+
+    snprintf(buffer, sizeof(buffer), "vc%d", index);
+    snprintf(path, sizeof(path), "<QEMU>/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);
+
+    adjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
+
+    scrolled_window = gtk_scrolled_window_new(NULL, adjustment);
+
+    gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
+
+    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 **/
@@ -446,6 +576,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();
@@ -472,6 +603,13 @@  static void gd_create_menus(GtkDisplayState *s)
     gtk_accel_map_add_entry("<QEMU>/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);