gtk: add virtual console support (v2)
[qemu/agraf.git] / ui / gtk.c
blob94f04612f06de3531b286078e57362546c93c773
1 /*
2 * GTK UI
4 * Copyright IBM, Corp. 2012
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
12 * Portions from gtk-vnc:
14 * GTK VNC Widget
16 * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
17 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
19 * This library is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU Lesser General Public
21 * License as published by the Free Software Foundation; either
22 * version 2.0 of the License, or (at your option) any later version.
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library; if not, write to the Free Software
31 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
34 #include <gtk/gtk.h>
35 #include <gdk/gdkkeysyms.h>
36 #include <vte/vte.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #include <sys/wait.h>
41 #include <pty.h>
42 #include <math.h>
44 #include "qemu-common.h"
45 #include "ui/console.h"
46 #include "sysemu/sysemu.h"
47 #include "qmp-commands.h"
48 #include "x_keymap.h"
49 #include "keymaps.h"
50 #include "char/char.h"
52 //#define DEBUG_GTK
54 #ifdef DEBUG_GTK
55 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
56 #else
57 #define DPRINTF(fmt, ...) do { } while (0)
58 #endif
60 #define MAX_VCS 10
62 typedef struct VirtualConsole
64 GtkWidget *menu_item;
65 GtkWidget *terminal;
66 GtkWidget *scrolled_window;
67 CharDriverState *chr;
68 int fd;
69 } VirtualConsole;
71 typedef struct GtkDisplayState
73 GtkWidget *window;
75 GtkWidget *menu_bar;
77 GtkWidget *file_menu_item;
78 GtkWidget *file_menu;
79 GtkWidget *quit_item;
81 GtkWidget *view_menu_item;
82 GtkWidget *view_menu;
83 GtkWidget *vga_item;
85 int nb_vcs;
86 VirtualConsole vc[MAX_VCS];
88 GtkWidget *show_tabs_item;
90 GtkWidget *vbox;
91 GtkWidget *notebook;
92 GtkWidget *drawing_area;
93 cairo_surface_t *surface;
94 DisplayChangeListener dcl;
95 DisplayState *ds;
96 int button_mask;
97 int last_x;
98 int last_y;
100 double scale_x;
101 double scale_y;
103 GdkCursor *null_cursor;
104 Notifier mouse_mode_notifier;
105 } GtkDisplayState;
107 static GtkDisplayState *global_state;
109 /** Utility Functions **/
111 static void gd_update_cursor(GtkDisplayState *s, gboolean override)
113 GdkWindow *window;
114 bool on_vga;
116 window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
118 on_vga = (gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0);
120 if ((override || on_vga) && kbd_mouse_is_absolute()) {
121 gdk_window_set_cursor(window, s->null_cursor);
122 } else {
123 gdk_window_set_cursor(window, NULL);
127 static void gd_update_caption(GtkDisplayState *s)
129 const char *status = "";
130 gchar *title;
132 if (!runstate_is_running()) {
133 status = " [Stopped]";
136 if (qemu_name) {
137 title = g_strdup_printf("QEMU (%s)%s", qemu_name, status);
138 } else {
139 title = g_strdup_printf("QEMU%s", status);
142 gtk_window_set_title(GTK_WINDOW(s->window), title);
144 g_free(title);
147 /** DisplayState Callbacks **/
149 static void gd_update(DisplayState *ds, int x, int y, int w, int h)
151 GtkDisplayState *s = ds->opaque;
152 int x1, x2, y1, y2;
154 DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
156 x1 = floor(x * s->scale_x);
157 y1 = floor(y * s->scale_y);
159 x2 = ceil(x * s->scale_x + w * s->scale_x);
160 y2 = ceil(y * s->scale_y + h * s->scale_y);
162 gtk_widget_queue_draw_area(s->drawing_area, x1, y1, (x2 - x1), (y2 - y1));
165 static void gd_refresh(DisplayState *ds)
167 vga_hw_update();
170 static void gd_resize(DisplayState *ds)
172 GtkDisplayState *s = ds->opaque;
173 cairo_format_t kind;
174 int stride;
176 DPRINTF("resize(width=%d, height=%d)\n",
177 ds_get_width(ds), ds_get_height(ds));
179 if (s->surface) {
180 cairo_surface_destroy(s->surface);
183 switch (ds->surface->pf.bits_per_pixel) {
184 case 8:
185 kind = CAIRO_FORMAT_A8;
186 break;
187 case 16:
188 kind = CAIRO_FORMAT_RGB16_565;
189 break;
190 case 32:
191 kind = CAIRO_FORMAT_RGB24;
192 break;
193 default:
194 g_assert_not_reached();
195 break;
198 stride = cairo_format_stride_for_width(kind, ds_get_width(ds));
199 g_assert(ds_get_linesize(ds) == stride);
201 s->surface = cairo_image_surface_create_for_data(ds_get_data(ds),
202 kind,
203 ds_get_width(ds),
204 ds_get_height(ds),
205 ds_get_linesize(ds));
207 gtk_widget_set_size_request(s->drawing_area,
208 ds_get_width(ds) * s->scale_x,
209 ds_get_height(ds) * s->scale_y);
212 /** QEMU Events **/
214 static void gd_change_runstate(void *opaque, int running, RunState state)
216 GtkDisplayState *s = opaque;
218 gd_update_caption(s);
221 static void gd_mouse_mode_change(Notifier *notify, void *data)
223 gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
224 FALSE);
227 /** GTK Events **/
229 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
230 void *opaque)
232 GtkDisplayState *s = opaque;
234 if (!no_quit) {
235 unregister_displaychangelistener(s->ds, &s->dcl);
236 qmp_quit(NULL);
237 return FALSE;
240 return TRUE;
243 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
245 GtkDisplayState *s = opaque;
246 int ww, wh;
247 int fbw, fbh;
249 fbw = ds_get_width(s->ds);
250 fbh = ds_get_height(s->ds);
252 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
254 cairo_rectangle(cr, 0, 0, ww, wh);
256 if (ww != fbw || wh != fbh) {
257 s->scale_x = (double)ww / fbw;
258 s->scale_y = (double)wh / fbh;
259 cairo_scale(cr, s->scale_x, s->scale_y);
260 } else {
261 s->scale_x = 1.0;
262 s->scale_y = 1.0;
265 cairo_set_source_surface(cr, s->surface, 0, 0);
266 cairo_paint(cr);
268 return TRUE;
271 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
272 void *opaque)
274 cairo_t *cr;
275 gboolean ret;
277 cr = gdk_cairo_create(gtk_widget_get_window(widget));
278 cairo_rectangle(cr,
279 expose->area.x,
280 expose->area.y,
281 expose->area.width,
282 expose->area.height);
283 cairo_clip(cr);
285 ret = gd_draw_event(widget, cr, opaque);
287 cairo_destroy(cr);
289 return ret;
292 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
293 void *opaque)
295 GtkDisplayState *s = opaque;
296 int dx, dy;
297 int x, y;
299 x = motion->x / s->scale_x;
300 y = motion->y / s->scale_y;
302 if (kbd_mouse_is_absolute()) {
303 dx = x * 0x7FFF / (ds_get_width(s->ds) - 1);
304 dy = y * 0x7FFF / (ds_get_height(s->ds) - 1);
305 } else if (s->last_x == -1 || s->last_y == -1) {
306 dx = 0;
307 dy = 0;
308 } else {
309 dx = x - s->last_x;
310 dy = y - s->last_y;
313 s->last_x = x;
314 s->last_y = y;
316 if (kbd_mouse_is_absolute()) {
317 kbd_mouse_event(dx, dy, 0, s->button_mask);
320 return TRUE;
323 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
324 void *opaque)
326 GtkDisplayState *s = opaque;
327 int dx, dy;
328 int n;
330 if (button->button == 1) {
331 n = 0x01;
332 } else if (button->button == 2) {
333 n = 0x04;
334 } else if (button->button == 3) {
335 n = 0x02;
336 } else {
337 n = 0x00;
340 if (button->type == GDK_BUTTON_PRESS) {
341 s->button_mask |= n;
342 } else if (button->type == GDK_BUTTON_RELEASE) {
343 s->button_mask &= ~n;
346 if (kbd_mouse_is_absolute()) {
347 dx = s->last_x * 0x7FFF / (ds_get_width(s->ds) - 1);
348 dy = s->last_y * 0x7FFF / (ds_get_height(s->ds) - 1);
349 } else {
350 dx = 0;
351 dy = 0;
354 kbd_mouse_event(dx, dy, 0, s->button_mask);
356 return TRUE;
359 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
361 int gdk_keycode;
362 int qemu_keycode;
364 gdk_keycode = key->hardware_keycode;
366 if (gdk_keycode < 9) {
367 qemu_keycode = 0;
368 } else if (gdk_keycode < 97) {
369 qemu_keycode = gdk_keycode - 8;
370 } else if (gdk_keycode < 158) {
371 qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
372 } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
373 qemu_keycode = 0x70;
374 } else if (gdk_keycode == 211) { /* backslash */
375 qemu_keycode = 0x73;
376 } else {
377 qemu_keycode = 0;
380 DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
381 gdk_keycode, qemu_keycode,
382 (key->type == GDK_KEY_PRESS) ? "down" : "up");
384 if (qemu_keycode & SCANCODE_GREY) {
385 kbd_put_keycode(SCANCODE_EMUL0);
388 if (key->type == GDK_KEY_PRESS) {
389 kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
390 } else if (key->type == GDK_KEY_RELEASE) {
391 kbd_put_keycode(qemu_keycode | SCANCODE_UP);
392 } else {
393 g_assert_not_reached();
396 return TRUE;
399 /** Window Menu Actions **/
401 static void gd_menu_quit(GtkMenuItem *item, void *opaque)
403 qmp_quit(NULL);
406 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
408 GtkDisplayState *s = opaque;
410 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
411 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
412 } else {
413 int i;
415 for (i = 0; i < s->nb_vcs; i++) {
416 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
417 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
418 break;
424 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
426 GtkDisplayState *s = opaque;
428 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
429 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
430 } else {
431 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
435 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
436 gpointer data)
438 GtkDisplayState *s = data;
439 guint last_page;
441 if (!gtk_widget_get_realized(s->notebook)) {
442 return;
445 last_page = gtk_notebook_get_current_page(nb);
447 if (last_page) {
448 gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
451 if (arg2 == 0) {
452 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
453 } else {
454 VirtualConsole *vc = &s->vc[arg2 - 1];
455 VteTerminal *term = VTE_TERMINAL(vc->terminal);
456 int width, height;
458 width = 80 * vte_terminal_get_char_width(term);
459 height = 25 * vte_terminal_get_char_height(term);
461 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
462 gtk_widget_set_size_request(vc->terminal, width, height);
465 gd_update_cursor(s, TRUE);
468 /** Virtual Console Callbacks **/
470 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
472 VirtualConsole *vc = chr->opaque;
474 return write(vc->fd, buf, len);
477 static int nb_vcs;
478 static CharDriverState *vcs[MAX_VCS];
480 static CharDriverState *gd_vc_handler(QemuOpts *opts)
482 CharDriverState *chr;
484 chr = g_malloc0(sizeof(*chr));
485 chr->chr_write = gd_vc_chr_write;
487 vcs[nb_vcs++] = chr;
489 return chr;
492 void early_gtk_display_init(void)
494 register_vc_handler(gd_vc_handler);
497 static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
499 VirtualConsole *vc = opaque;
500 uint8_t buffer[1024];
501 ssize_t len;
503 len = read(vc->fd, buffer, sizeof(buffer));
504 if (len <= 0) {
505 return FALSE;
508 qemu_chr_be_write(vc->chr, buffer, len);
510 return TRUE;
513 static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group)
515 const char *label;
516 char buffer[32];
517 char path[32];
518 VtePty *pty;
519 GIOChannel *chan;
520 GtkWidget *scrolled_window;
521 GtkAdjustment *vadjustment;
522 int master_fd, slave_fd, ret;
523 struct termios tty;
525 snprintf(buffer, sizeof(buffer), "vc%d", index);
526 snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
528 vc->chr = vcs[index];
530 if (vc->chr->label) {
531 label = vc->chr->label;
532 } else {
533 label = buffer;
536 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
537 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
538 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
539 gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
541 vc->terminal = vte_terminal_new();
543 ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
544 g_assert(ret != -1);
546 /* Set raw attributes on the pty. */
547 tcgetattr(slave_fd, &tty);
548 cfmakeraw(&tty);
549 tcsetattr(slave_fd, TCSAFLUSH, &tty);
551 pty = vte_pty_new_foreign(master_fd, NULL);
553 vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
555 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
557 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
559 scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
560 gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
562 vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
564 vc->fd = slave_fd;
565 vc->chr->opaque = vc;
566 vc->scrolled_window = scrolled_window;
568 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
569 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
571 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
572 g_signal_connect(vc->menu_item, "activate",
573 G_CALLBACK(gd_menu_switch_vc), s);
575 gtk_menu_append(GTK_MENU(s->view_menu), vc->menu_item);
577 qemu_chr_generic_open(vc->chr);
578 if (vc->chr->init) {
579 vc->chr->init(vc->chr);
582 chan = g_io_channel_unix_new(vc->fd);
583 g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
585 return group;
588 /** Window Creation **/
590 static void gd_connect_signals(GtkDisplayState *s)
592 g_signal_connect(s->show_tabs_item, "activate",
593 G_CALLBACK(gd_menu_show_tabs), s);
595 g_signal_connect(s->window, "delete-event",
596 G_CALLBACK(gd_window_close), s);
598 g_signal_connect(s->drawing_area, "expose-event",
599 G_CALLBACK(gd_expose_event), s);
600 g_signal_connect(s->drawing_area, "motion-notify-event",
601 G_CALLBACK(gd_motion_event), s);
602 g_signal_connect(s->drawing_area, "button-press-event",
603 G_CALLBACK(gd_button_event), s);
604 g_signal_connect(s->drawing_area, "button-release-event",
605 G_CALLBACK(gd_button_event), s);
606 g_signal_connect(s->drawing_area, "key-press-event",
607 G_CALLBACK(gd_key_event), s);
608 g_signal_connect(s->drawing_area, "key-release-event",
609 G_CALLBACK(gd_key_event), s);
611 g_signal_connect(s->quit_item, "activate",
612 G_CALLBACK(gd_menu_quit), s);
613 g_signal_connect(s->vga_item, "activate",
614 G_CALLBACK(gd_menu_switch_vc), s);
615 g_signal_connect(s->notebook, "switch-page",
616 G_CALLBACK(gd_change_page), s);
619 static void gd_create_menus(GtkDisplayState *s)
621 GtkStockItem item;
622 GtkAccelGroup *accel_group;
623 GSList *group = NULL;
624 GtkWidget *separator;
625 int i;
627 accel_group = gtk_accel_group_new();
628 s->file_menu = gtk_menu_new();
629 gtk_menu_set_accel_group(GTK_MENU(s->file_menu), accel_group);
630 s->file_menu_item = gtk_menu_item_new_with_mnemonic("_File");
632 s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
633 gtk_stock_lookup(GTK_STOCK_QUIT, &item);
634 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
635 "<QEMU>/File/Quit");
636 gtk_accel_map_add_entry("<QEMU>/File/Quit", item.keyval, item.modifier);
638 s->view_menu = gtk_menu_new();
639 gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group);
640 s->view_menu_item = gtk_menu_item_new_with_mnemonic("_View");
642 separator = gtk_separator_menu_item_new();
643 gtk_menu_append(GTK_MENU(s->view_menu), separator);
645 s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
646 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
647 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
648 "<QEMU>/View/VGA");
649 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
650 gtk_menu_append(GTK_MENU(s->view_menu), s->vga_item);
652 for (i = 0; i < nb_vcs; i++) {
653 VirtualConsole *vc = &s->vc[i];
655 group = gd_vc_init(s, vc, i, group);
656 s->nb_vcs++;
659 separator = gtk_separator_menu_item_new();
660 gtk_menu_append(GTK_MENU(s->view_menu), separator);
662 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic("Show _Tabs");
663 gtk_menu_append(GTK_MENU(s->view_menu), s->show_tabs_item);
665 g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
666 gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
668 gtk_menu_append(GTK_MENU(s->file_menu), s->quit_item);
669 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->file_menu_item), s->file_menu);
670 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->file_menu_item);
672 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
673 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
676 void gtk_display_init(DisplayState *ds)
678 GtkDisplayState *s = g_malloc0(sizeof(*s));
680 gtk_init(NULL, NULL);
682 ds->opaque = s;
683 s->ds = ds;
684 s->dcl.dpy_gfx_update = gd_update;
685 s->dcl.dpy_gfx_resize = gd_resize;
686 s->dcl.dpy_refresh = gd_refresh;
688 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
689 s->vbox = gtk_vbox_new(FALSE, 0);
690 s->notebook = gtk_notebook_new();
691 s->drawing_area = gtk_drawing_area_new();
692 s->menu_bar = gtk_menu_bar_new();
694 s->scale_x = 1.0;
695 s->scale_y = 1.0;
697 s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
699 s->mouse_mode_notifier.notify = gd_mouse_mode_change;
700 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
701 qemu_add_vm_change_state_handler(gd_change_runstate, s);
703 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
705 gd_create_menus(s);
707 gd_connect_signals(s);
709 gtk_widget_add_events(s->drawing_area,
710 GDK_POINTER_MOTION_MASK |
711 GDK_BUTTON_PRESS_MASK |
712 GDK_BUTTON_RELEASE_MASK |
713 GDK_BUTTON_MOTION_MASK |
714 GDK_SCROLL_MASK |
715 GDK_KEY_PRESS_MASK);
716 gtk_widget_set_double_buffered(s->drawing_area, FALSE);
717 gtk_widget_set_can_focus(s->drawing_area, TRUE);
719 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
720 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
722 gtk_window_set_resizable(GTK_WINDOW(s->window), FALSE);
724 gd_update_caption(s);
726 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
727 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
729 gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
731 gtk_widget_show_all(s->window);
733 register_displaychangelistener(ds, &s->dcl);
735 global_state = s;