gtk: add support for input grabbing (v2)
[qemu/pbrook.git] / ui / gtk.c
blob0c1ec4db1031798a231d0815bb82b3c8392948e2
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 *grab_item;
84 GtkWidget *grab_on_hover_item;
85 GtkWidget *vga_item;
87 int nb_vcs;
88 VirtualConsole vc[MAX_VCS];
90 GtkWidget *show_tabs_item;
92 GtkWidget *vbox;
93 GtkWidget *notebook;
94 GtkWidget *drawing_area;
95 cairo_surface_t *surface;
96 DisplayChangeListener dcl;
97 DisplayState *ds;
98 int button_mask;
99 int last_x;
100 int last_y;
102 double scale_x;
103 double scale_y;
105 GdkCursor *null_cursor;
106 Notifier mouse_mode_notifier;
107 } GtkDisplayState;
109 static GtkDisplayState *global_state;
111 /** Utility Functions **/
113 static bool gd_is_grab_active(GtkDisplayState *s)
115 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
118 static bool gd_grab_on_hover(GtkDisplayState *s)
120 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
123 static bool gd_on_vga(GtkDisplayState *s)
125 return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
128 static void gd_update_cursor(GtkDisplayState *s, gboolean override)
130 GdkWindow *window;
131 bool on_vga;
133 window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
135 on_vga = gd_on_vga(s);
137 if ((override || on_vga) &&
138 (kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
139 gdk_window_set_cursor(window, s->null_cursor);
140 } else {
141 gdk_window_set_cursor(window, NULL);
145 static void gd_update_caption(GtkDisplayState *s)
147 const char *status = "";
148 gchar *title;
149 const char *grab = "";
151 if (gd_is_grab_active(s)) {
152 grab = " - Press Ctrl+Alt+G to release grab";
155 if (!runstate_is_running()) {
156 status = " [Stopped]";
159 if (qemu_name) {
160 title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
161 } else {
162 title = g_strdup_printf("QEMU%s%s", status, grab);
165 gtk_window_set_title(GTK_WINDOW(s->window), title);
167 g_free(title);
170 /** DisplayState Callbacks **/
172 static void gd_update(DisplayState *ds, int x, int y, int w, int h)
174 GtkDisplayState *s = ds->opaque;
175 int x1, x2, y1, y2;
177 DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
179 x1 = floor(x * s->scale_x);
180 y1 = floor(y * s->scale_y);
182 x2 = ceil(x * s->scale_x + w * s->scale_x);
183 y2 = ceil(y * s->scale_y + h * s->scale_y);
185 gtk_widget_queue_draw_area(s->drawing_area, x1, y1, (x2 - x1), (y2 - y1));
188 static void gd_refresh(DisplayState *ds)
190 vga_hw_update();
193 static void gd_resize(DisplayState *ds)
195 GtkDisplayState *s = ds->opaque;
196 cairo_format_t kind;
197 int stride;
199 DPRINTF("resize(width=%d, height=%d)\n",
200 ds_get_width(ds), ds_get_height(ds));
202 if (s->surface) {
203 cairo_surface_destroy(s->surface);
206 switch (ds->surface->pf.bits_per_pixel) {
207 case 8:
208 kind = CAIRO_FORMAT_A8;
209 break;
210 case 16:
211 kind = CAIRO_FORMAT_RGB16_565;
212 break;
213 case 32:
214 kind = CAIRO_FORMAT_RGB24;
215 break;
216 default:
217 g_assert_not_reached();
218 break;
221 stride = cairo_format_stride_for_width(kind, ds_get_width(ds));
222 g_assert(ds_get_linesize(ds) == stride);
224 s->surface = cairo_image_surface_create_for_data(ds_get_data(ds),
225 kind,
226 ds_get_width(ds),
227 ds_get_height(ds),
228 ds_get_linesize(ds));
230 gtk_widget_set_size_request(s->drawing_area,
231 ds_get_width(ds) * s->scale_x,
232 ds_get_height(ds) * s->scale_y);
235 /** QEMU Events **/
237 static void gd_change_runstate(void *opaque, int running, RunState state)
239 GtkDisplayState *s = opaque;
241 gd_update_caption(s);
244 static void gd_mouse_mode_change(Notifier *notify, void *data)
246 gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
247 FALSE);
250 /** GTK Events **/
252 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
253 void *opaque)
255 GtkDisplayState *s = opaque;
257 if (!no_quit) {
258 unregister_displaychangelistener(s->ds, &s->dcl);
259 qmp_quit(NULL);
260 return FALSE;
263 return TRUE;
266 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
268 GtkDisplayState *s = opaque;
269 int ww, wh;
270 int fbw, fbh;
272 fbw = ds_get_width(s->ds);
273 fbh = ds_get_height(s->ds);
275 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
277 cairo_rectangle(cr, 0, 0, ww, wh);
279 if (ww != fbw || wh != fbh) {
280 s->scale_x = (double)ww / fbw;
281 s->scale_y = (double)wh / fbh;
282 cairo_scale(cr, s->scale_x, s->scale_y);
283 } else {
284 s->scale_x = 1.0;
285 s->scale_y = 1.0;
288 fbw *= s->scale_x;
289 fbh *= s->scale_y;
291 cairo_set_source_surface(cr, s->surface, 0, 0);
292 cairo_paint(cr);
294 return TRUE;
297 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
298 void *opaque)
300 cairo_t *cr;
301 gboolean ret;
303 cr = gdk_cairo_create(gtk_widget_get_window(widget));
304 cairo_rectangle(cr,
305 expose->area.x,
306 expose->area.y,
307 expose->area.width,
308 expose->area.height);
309 cairo_clip(cr);
311 ret = gd_draw_event(widget, cr, opaque);
313 cairo_destroy(cr);
315 return ret;
318 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
319 void *opaque)
321 GtkDisplayState *s = opaque;
322 int dx, dy;
323 int x, y;
325 x = motion->x / s->scale_x;
326 y = motion->y / s->scale_y;
328 if (kbd_mouse_is_absolute()) {
329 dx = x * 0x7FFF / (ds_get_width(s->ds) - 1);
330 dy = y * 0x7FFF / (ds_get_height(s->ds) - 1);
331 } else if (s->last_x == -1 || s->last_y == -1) {
332 dx = 0;
333 dy = 0;
334 } else {
335 dx = x - s->last_x;
336 dy = y - s->last_y;
339 s->last_x = x;
340 s->last_y = y;
342 if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
343 kbd_mouse_event(dx, dy, 0, s->button_mask);
346 if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
347 GdkDrawable *drawable = GDK_DRAWABLE(gtk_widget_get_window(s->drawing_area));
348 GdkDisplay *display = gdk_drawable_get_display(drawable);
349 GdkScreen *screen = gdk_drawable_get_screen(drawable);
350 int x = (int)motion->x_root;
351 int y = (int)motion->y_root;
353 /* In relative mode check to see if client pointer hit
354 * one of the screen edges, and if so move it back by
355 * 200 pixels. This is important because the pointer
356 * in the server doesn't correspond 1-for-1, and so
357 * may still be only half way across the screen. Without
358 * this warp, the server pointer would thus appear to hit
359 * an invisible wall */
360 if (x == 0) {
361 x += 200;
363 if (y == 0) {
364 y += 200;
366 if (x == (gdk_screen_get_width(screen) - 1)) {
367 x -= 200;
369 if (y == (gdk_screen_get_height(screen) - 1)) {
370 y -= 200;
373 if (x != (int)motion->x_root || y != (int)motion->y_root) {
374 gdk_display_warp_pointer(display, screen, x, y);
375 s->last_x = -1;
376 s->last_y = -1;
377 return FALSE;
380 return TRUE;
383 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
384 void *opaque)
386 GtkDisplayState *s = opaque;
387 int dx, dy;
388 int n;
390 if (button->button == 1) {
391 n = 0x01;
392 } else if (button->button == 2) {
393 n = 0x04;
394 } else if (button->button == 3) {
395 n = 0x02;
396 } else {
397 n = 0x00;
400 if (button->type == GDK_BUTTON_PRESS) {
401 s->button_mask |= n;
402 } else if (button->type == GDK_BUTTON_RELEASE) {
403 s->button_mask &= ~n;
406 if (kbd_mouse_is_absolute()) {
407 dx = s->last_x * 0x7FFF / (ds_get_width(s->ds) - 1);
408 dy = s->last_y * 0x7FFF / (ds_get_height(s->ds) - 1);
409 } else {
410 dx = 0;
411 dy = 0;
414 kbd_mouse_event(dx, dy, 0, s->button_mask);
416 return TRUE;
419 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
421 int gdk_keycode;
422 int qemu_keycode;
424 gdk_keycode = key->hardware_keycode;
426 if (gdk_keycode < 9) {
427 qemu_keycode = 0;
428 } else if (gdk_keycode < 97) {
429 qemu_keycode = gdk_keycode - 8;
430 } else if (gdk_keycode < 158) {
431 qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
432 } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
433 qemu_keycode = 0x70;
434 } else if (gdk_keycode == 211) { /* backslash */
435 qemu_keycode = 0x73;
436 } else {
437 qemu_keycode = 0;
440 DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
441 gdk_keycode, qemu_keycode,
442 (key->type == GDK_KEY_PRESS) ? "down" : "up");
444 if (qemu_keycode & SCANCODE_GREY) {
445 kbd_put_keycode(SCANCODE_EMUL0);
448 if (key->type == GDK_KEY_PRESS) {
449 kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
450 } else if (key->type == GDK_KEY_RELEASE) {
451 kbd_put_keycode(qemu_keycode | SCANCODE_UP);
452 } else {
453 g_assert_not_reached();
456 return TRUE;
459 /** Window Menu Actions **/
461 static void gd_menu_quit(GtkMenuItem *item, void *opaque)
463 qmp_quit(NULL);
466 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
468 GtkDisplayState *s = opaque;
470 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
471 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
472 } else {
473 int i;
475 for (i = 0; i < s->nb_vcs; i++) {
476 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
477 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
478 break;
484 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
486 GtkDisplayState *s = opaque;
488 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
489 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
490 } else {
491 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
495 static void gd_grab_keyboard(GtkDisplayState *s)
497 gdk_keyboard_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
498 FALSE,
499 GDK_CURRENT_TIME);
502 static void gd_ungrab_keyboard(GtkDisplayState *s)
504 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
507 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
509 GtkDisplayState *s = opaque;
511 if (gd_is_grab_active(s)) {
512 gd_grab_keyboard(s);
513 gdk_pointer_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
514 FALSE, /* All events to come to our window directly */
515 GDK_POINTER_MOTION_MASK |
516 GDK_BUTTON_PRESS_MASK |
517 GDK_BUTTON_RELEASE_MASK |
518 GDK_BUTTON_MOTION_MASK |
519 GDK_SCROLL_MASK,
520 NULL, /* Allow cursor to move over entire desktop */
521 s->null_cursor,
522 GDK_CURRENT_TIME);
523 } else {
524 gd_ungrab_keyboard(s);
525 gdk_pointer_ungrab(GDK_CURRENT_TIME);
528 gd_update_caption(s);
529 gd_update_cursor(s, FALSE);
532 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
533 gpointer data)
535 GtkDisplayState *s = data;
536 guint last_page;
537 gboolean on_vga;
539 if (!gtk_widget_get_realized(s->notebook)) {
540 return;
543 last_page = gtk_notebook_get_current_page(nb);
545 if (last_page) {
546 gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
549 on_vga = arg2 == 0;
551 if (!on_vga) {
552 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
553 FALSE);
556 if (arg2 == 0) {
557 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
558 } else {
559 VirtualConsole *vc = &s->vc[arg2 - 1];
560 VteTerminal *term = VTE_TERMINAL(vc->terminal);
561 int width, height;
563 width = 80 * vte_terminal_get_char_width(term);
564 height = 25 * vte_terminal_get_char_height(term);
566 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
567 gtk_widget_set_size_request(vc->terminal, width, height);
570 gtk_widget_set_sensitive(s->grab_item, on_vga);
572 gd_update_cursor(s, TRUE);
575 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
577 GtkDisplayState *s = data;
579 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
580 gd_grab_keyboard(s);
583 return TRUE;
586 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
588 GtkDisplayState *s = data;
590 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
591 gd_ungrab_keyboard(s);
594 return TRUE;
597 /** Virtual Console Callbacks **/
599 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
601 VirtualConsole *vc = chr->opaque;
603 return write(vc->fd, buf, len);
606 static int nb_vcs;
607 static CharDriverState *vcs[MAX_VCS];
609 static CharDriverState *gd_vc_handler(QemuOpts *opts)
611 CharDriverState *chr;
613 chr = g_malloc0(sizeof(*chr));
614 chr->chr_write = gd_vc_chr_write;
616 vcs[nb_vcs++] = chr;
618 return chr;
621 void early_gtk_display_init(void)
623 register_vc_handler(gd_vc_handler);
626 static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
628 VirtualConsole *vc = opaque;
629 uint8_t buffer[1024];
630 ssize_t len;
632 len = read(vc->fd, buffer, sizeof(buffer));
633 if (len <= 0) {
634 return FALSE;
637 qemu_chr_be_write(vc->chr, buffer, len);
639 return TRUE;
642 static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group)
644 const char *label;
645 char buffer[32];
646 char path[32];
647 VtePty *pty;
648 GIOChannel *chan;
649 GtkWidget *scrolled_window;
650 GtkAdjustment *vadjustment;
651 int master_fd, slave_fd, ret;
652 struct termios tty;
654 snprintf(buffer, sizeof(buffer), "vc%d", index);
655 snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
657 vc->chr = vcs[index];
659 if (vc->chr->label) {
660 label = vc->chr->label;
661 } else {
662 label = buffer;
665 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
666 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
667 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
668 gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
670 vc->terminal = vte_terminal_new();
672 ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
673 g_assert(ret != -1);
675 /* Set raw attributes on the pty. */
676 tcgetattr(slave_fd, &tty);
677 cfmakeraw(&tty);
678 tcsetattr(slave_fd, TCSAFLUSH, &tty);
680 pty = vte_pty_new_foreign(master_fd, NULL);
682 vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
684 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
686 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
688 scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
689 gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
691 vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
693 vc->fd = slave_fd;
694 vc->chr->opaque = vc;
695 vc->scrolled_window = scrolled_window;
697 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
698 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
700 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
701 g_signal_connect(vc->menu_item, "activate",
702 G_CALLBACK(gd_menu_switch_vc), s);
704 gtk_menu_append(GTK_MENU(s->view_menu), vc->menu_item);
706 qemu_chr_generic_open(vc->chr);
707 if (vc->chr->init) {
708 vc->chr->init(vc->chr);
711 chan = g_io_channel_unix_new(vc->fd);
712 g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
714 return group;
717 /** Window Creation **/
719 static void gd_connect_signals(GtkDisplayState *s)
721 g_signal_connect(s->show_tabs_item, "activate",
722 G_CALLBACK(gd_menu_show_tabs), s);
724 g_signal_connect(s->window, "delete-event",
725 G_CALLBACK(gd_window_close), s);
727 g_signal_connect(s->drawing_area, "expose-event",
728 G_CALLBACK(gd_expose_event), s);
729 g_signal_connect(s->drawing_area, "motion-notify-event",
730 G_CALLBACK(gd_motion_event), s);
731 g_signal_connect(s->drawing_area, "button-press-event",
732 G_CALLBACK(gd_button_event), s);
733 g_signal_connect(s->drawing_area, "button-release-event",
734 G_CALLBACK(gd_button_event), s);
735 g_signal_connect(s->drawing_area, "key-press-event",
736 G_CALLBACK(gd_key_event), s);
737 g_signal_connect(s->drawing_area, "key-release-event",
738 G_CALLBACK(gd_key_event), s);
740 g_signal_connect(s->quit_item, "activate",
741 G_CALLBACK(gd_menu_quit), s);
742 g_signal_connect(s->vga_item, "activate",
743 G_CALLBACK(gd_menu_switch_vc), s);
744 g_signal_connect(s->grab_item, "activate",
745 G_CALLBACK(gd_menu_grab_input), s);
746 g_signal_connect(s->notebook, "switch-page",
747 G_CALLBACK(gd_change_page), s);
748 g_signal_connect(s->drawing_area, "enter-notify-event",
749 G_CALLBACK(gd_enter_event), s);
750 g_signal_connect(s->drawing_area, "leave-notify-event",
751 G_CALLBACK(gd_leave_event), s);
754 static void gd_create_menus(GtkDisplayState *s)
756 GtkStockItem item;
757 GtkAccelGroup *accel_group;
758 GSList *group = NULL;
759 GtkWidget *separator;
760 int i;
762 accel_group = gtk_accel_group_new();
763 s->file_menu = gtk_menu_new();
764 gtk_menu_set_accel_group(GTK_MENU(s->file_menu), accel_group);
765 s->file_menu_item = gtk_menu_item_new_with_mnemonic("_File");
767 s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
768 gtk_stock_lookup(GTK_STOCK_QUIT, &item);
769 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
770 "<QEMU>/File/Quit");
771 gtk_accel_map_add_entry("<QEMU>/File/Quit", item.keyval, item.modifier);
773 s->view_menu = gtk_menu_new();
774 gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group);
775 s->view_menu_item = gtk_menu_item_new_with_mnemonic("_View");
777 s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic("Grab On _Hover");
778 gtk_menu_append(GTK_MENU(s->view_menu), s->grab_on_hover_item);
780 s->grab_item = gtk_check_menu_item_new_with_mnemonic("_Grab Input");
781 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
782 "<QEMU>/View/Grab Input");
783 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK);
784 gtk_menu_append(GTK_MENU(s->view_menu), s->grab_item);
786 separator = gtk_separator_menu_item_new();
787 gtk_menu_append(GTK_MENU(s->view_menu), separator);
789 s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
790 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
791 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
792 "<QEMU>/View/VGA");
793 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
794 gtk_menu_append(GTK_MENU(s->view_menu), s->vga_item);
796 for (i = 0; i < nb_vcs; i++) {
797 VirtualConsole *vc = &s->vc[i];
799 group = gd_vc_init(s, vc, i, group);
800 s->nb_vcs++;
803 separator = gtk_separator_menu_item_new();
804 gtk_menu_append(GTK_MENU(s->view_menu), separator);
806 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic("Show _Tabs");
807 gtk_menu_append(GTK_MENU(s->view_menu), s->show_tabs_item);
809 g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
810 gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
812 gtk_menu_append(GTK_MENU(s->file_menu), s->quit_item);
813 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->file_menu_item), s->file_menu);
814 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->file_menu_item);
816 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
817 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
820 void gtk_display_init(DisplayState *ds)
822 GtkDisplayState *s = g_malloc0(sizeof(*s));
824 gtk_init(NULL, NULL);
826 ds->opaque = s;
827 s->ds = ds;
828 s->dcl.dpy_gfx_update = gd_update;
829 s->dcl.dpy_gfx_resize = gd_resize;
830 s->dcl.dpy_refresh = gd_refresh;
832 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
833 s->vbox = gtk_vbox_new(FALSE, 0);
834 s->notebook = gtk_notebook_new();
835 s->drawing_area = gtk_drawing_area_new();
836 s->menu_bar = gtk_menu_bar_new();
838 s->scale_x = 1.0;
839 s->scale_y = 1.0;
841 s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
843 s->mouse_mode_notifier.notify = gd_mouse_mode_change;
844 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
845 qemu_add_vm_change_state_handler(gd_change_runstate, s);
847 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
849 gd_create_menus(s);
851 gd_connect_signals(s);
853 gtk_widget_add_events(s->drawing_area,
854 GDK_POINTER_MOTION_MASK |
855 GDK_BUTTON_PRESS_MASK |
856 GDK_BUTTON_RELEASE_MASK |
857 GDK_BUTTON_MOTION_MASK |
858 GDK_ENTER_NOTIFY_MASK |
859 GDK_LEAVE_NOTIFY_MASK |
860 GDK_SCROLL_MASK |
861 GDK_KEY_PRESS_MASK);
862 gtk_widget_set_double_buffered(s->drawing_area, FALSE);
863 gtk_widget_set_can_focus(s->drawing_area, TRUE);
865 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
866 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
868 gtk_window_set_resizable(GTK_WINDOW(s->window), FALSE);
870 gd_update_caption(s);
872 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
873 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
875 gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
877 gtk_widget_show_all(s->window);
879 register_displaychangelistener(ds, &s->dcl);
881 global_state = s;