gtk: add translation support (v5)
[qemu/cris-port.git] / ui / gtk.c
blobffa9baacf16ad40a6b5afc10ea4f5ae8e441b04c
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 #define GETTEXT_PACKAGE "qemu"
35 #define LOCALEDIR "po"
37 #include <gtk/gtk.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <glib/gi18n.h>
40 #include <vte/vte.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/un.h>
44 #include <sys/wait.h>
45 #include <pty.h>
46 #include <math.h>
48 #include "qemu-common.h"
49 #include "ui/console.h"
50 #include "sysemu/sysemu.h"
51 #include "qmp-commands.h"
52 #include "x_keymap.h"
53 #include "keymaps.h"
54 #include "char/char.h"
56 //#define DEBUG_GTK
58 #ifdef DEBUG_GTK
59 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
60 #else
61 #define DPRINTF(fmt, ...) do { } while (0)
62 #endif
64 #define MAX_VCS 10
66 typedef struct VirtualConsole
68 GtkWidget *menu_item;
69 GtkWidget *terminal;
70 GtkWidget *scrolled_window;
71 CharDriverState *chr;
72 int fd;
73 } VirtualConsole;
75 typedef struct GtkDisplayState
77 GtkWidget *window;
79 GtkWidget *menu_bar;
81 GtkWidget *file_menu_item;
82 GtkWidget *file_menu;
83 GtkWidget *quit_item;
85 GtkWidget *view_menu_item;
86 GtkWidget *view_menu;
87 GtkWidget *full_screen_item;
88 GtkWidget *zoom_in_item;
89 GtkWidget *zoom_out_item;
90 GtkWidget *zoom_fixed_item;
91 GtkWidget *zoom_fit_item;
92 GtkWidget *grab_item;
93 GtkWidget *grab_on_hover_item;
94 GtkWidget *vga_item;
96 int nb_vcs;
97 VirtualConsole vc[MAX_VCS];
99 GtkWidget *show_tabs_item;
101 GtkWidget *vbox;
102 GtkWidget *notebook;
103 GtkWidget *drawing_area;
104 cairo_surface_t *surface;
105 DisplayChangeListener dcl;
106 DisplayState *ds;
107 int button_mask;
108 int last_x;
109 int last_y;
111 double scale_x;
112 double scale_y;
113 gboolean full_screen;
115 GdkCursor *null_cursor;
116 Notifier mouse_mode_notifier;
117 gboolean free_scale;
118 } GtkDisplayState;
120 static GtkDisplayState *global_state;
122 /** Utility Functions **/
124 static bool gd_is_grab_active(GtkDisplayState *s)
126 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
129 static bool gd_grab_on_hover(GtkDisplayState *s)
131 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
134 static bool gd_on_vga(GtkDisplayState *s)
136 return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
139 static void gd_update_cursor(GtkDisplayState *s, gboolean override)
141 GdkWindow *window;
142 bool on_vga;
144 window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
146 on_vga = gd_on_vga(s);
148 if ((override || on_vga) &&
149 (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
150 gdk_window_set_cursor(window, s->null_cursor);
151 } else {
152 gdk_window_set_cursor(window, NULL);
156 static void gd_update_caption(GtkDisplayState *s)
158 const char *status = "";
159 gchar *title;
160 const char *grab = "";
162 if (gd_is_grab_active(s)) {
163 grab = " - Press Ctrl+Alt+G to release grab";
166 if (!runstate_is_running()) {
167 status = " [Stopped]";
170 if (qemu_name) {
171 title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
172 } else {
173 title = g_strdup_printf("QEMU%s%s", status, grab);
176 gtk_window_set_title(GTK_WINDOW(s->window), title);
178 g_free(title);
181 /** DisplayState Callbacks **/
183 static void gd_update(DisplayState *ds, int x, int y, int w, int h)
185 GtkDisplayState *s = ds->opaque;
186 int x1, x2, y1, y2;
187 int mx, my;
188 int fbw, fbh;
189 int ww, wh;
191 DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
193 x1 = floor(x * s->scale_x);
194 y1 = floor(y * s->scale_y);
196 x2 = ceil(x * s->scale_x + w * s->scale_x);
197 y2 = ceil(y * s->scale_y + h * s->scale_y);
199 fbw = ds_get_width(s->ds) * s->scale_x;
200 fbh = ds_get_height(s->ds) * s->scale_y;
202 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
204 mx = my = 0;
205 if (ww > fbw) {
206 mx = (ww - fbw) / 2;
208 if (wh > fbh) {
209 my = (wh - fbh) / 2;
212 gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1));
215 static void gd_refresh(DisplayState *ds)
217 vga_hw_update();
220 static void gd_resize(DisplayState *ds)
222 GtkDisplayState *s = ds->opaque;
223 cairo_format_t kind;
224 int stride;
226 DPRINTF("resize(width=%d, height=%d)\n",
227 ds_get_width(ds), ds_get_height(ds));
229 if (s->surface) {
230 cairo_surface_destroy(s->surface);
233 switch (ds->surface->pf.bits_per_pixel) {
234 case 8:
235 kind = CAIRO_FORMAT_A8;
236 break;
237 case 16:
238 kind = CAIRO_FORMAT_RGB16_565;
239 break;
240 case 32:
241 kind = CAIRO_FORMAT_RGB24;
242 break;
243 default:
244 g_assert_not_reached();
245 break;
248 stride = cairo_format_stride_for_width(kind, ds_get_width(ds));
249 g_assert(ds_get_linesize(ds) == stride);
251 s->surface = cairo_image_surface_create_for_data(ds_get_data(ds),
252 kind,
253 ds_get_width(ds),
254 ds_get_height(ds),
255 ds_get_linesize(ds));
257 if (!s->full_screen) {
258 GtkRequisition req;
259 double sx, sy;
261 if (s->free_scale) {
262 sx = s->scale_x;
263 sy = s->scale_y;
265 s->scale_y = 1.0;
266 s->scale_x = 1.0;
267 } else {
268 sx = 1.0;
269 sy = 1.0;
272 gtk_widget_set_size_request(s->drawing_area,
273 ds_get_width(ds) * s->scale_x,
274 ds_get_height(ds) * s->scale_y);
275 gtk_widget_size_request(s->vbox, &req);
277 gtk_window_resize(GTK_WINDOW(s->window),
278 req.width * sx, req.height * sy);
282 /** QEMU Events **/
284 static void gd_change_runstate(void *opaque, int running, RunState state)
286 GtkDisplayState *s = opaque;
288 gd_update_caption(s);
291 static void gd_mouse_mode_change(Notifier *notify, void *data)
293 gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
294 FALSE);
297 /** GTK Events **/
299 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
300 void *opaque)
302 GtkDisplayState *s = opaque;
304 if (!no_quit) {
305 unregister_displaychangelistener(s->ds, &s->dcl);
306 qmp_quit(NULL);
307 return FALSE;
310 return TRUE;
313 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
315 GtkDisplayState *s = opaque;
316 int mx, my;
317 int ww, wh;
318 int fbw, fbh;
320 if (!gtk_widget_get_realized(widget)) {
321 return FALSE;
324 fbw = ds_get_width(s->ds);
325 fbh = ds_get_height(s->ds);
327 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
329 if (s->full_screen) {
330 s->scale_x = (double)ww / fbw;
331 s->scale_y = (double)wh / fbh;
332 } else if (s->free_scale) {
333 double sx, sy;
335 sx = (double)ww / fbw;
336 sy = (double)wh / fbh;
338 s->scale_x = s->scale_y = MIN(sx, sy);
341 fbw *= s->scale_x;
342 fbh *= s->scale_y;
344 mx = my = 0;
345 if (ww > fbw) {
346 mx = (ww - fbw) / 2;
348 if (wh > fbh) {
349 my = (wh - fbh) / 2;
352 cairo_rectangle(cr, 0, 0, ww, wh);
354 /* Optionally cut out the inner area where the pixmap
355 will be drawn. This avoids 'flashing' since we're
356 not double-buffering. Note we're using the undocumented
357 behaviour of drawing the rectangle from right to left
358 to cut out the whole */
359 cairo_rectangle(cr, mx + fbw, my,
360 -1 * fbw, fbh);
361 cairo_fill(cr);
363 cairo_scale(cr, s->scale_x, s->scale_y);
364 cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y);
365 cairo_paint(cr);
367 return TRUE;
370 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
371 void *opaque)
373 cairo_t *cr;
374 gboolean ret;
376 cr = gdk_cairo_create(gtk_widget_get_window(widget));
377 cairo_rectangle(cr,
378 expose->area.x,
379 expose->area.y,
380 expose->area.width,
381 expose->area.height);
382 cairo_clip(cr);
384 ret = gd_draw_event(widget, cr, opaque);
386 cairo_destroy(cr);
388 return ret;
391 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
392 void *opaque)
394 GtkDisplayState *s = opaque;
395 int dx, dy;
396 int x, y;
397 int mx, my;
398 int fbh, fbw;
399 int ww, wh;
401 fbw = ds_get_width(s->ds) * s->scale_x;
402 fbh = ds_get_height(s->ds) * s->scale_y;
404 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
406 mx = my = 0;
407 if (ww > fbw) {
408 mx = (ww - fbw) / 2;
410 if (wh > fbh) {
411 my = (wh - fbh) / 2;
414 x = (motion->x - mx) / s->scale_x;
415 y = (motion->y - my) / s->scale_y;
417 if (x < 0 || y < 0 ||
418 x >= ds_get_width(s->ds) ||
419 y >= ds_get_height(s->ds)) {
420 return TRUE;
423 if (kbd_mouse_is_absolute()) {
424 dx = x * 0x7FFF / (ds_get_width(s->ds) - 1);
425 dy = y * 0x7FFF / (ds_get_height(s->ds) - 1);
426 } else if (s->last_x == -1 || s->last_y == -1) {
427 dx = 0;
428 dy = 0;
429 } else {
430 dx = x - s->last_x;
431 dy = y - s->last_y;
434 s->last_x = x;
435 s->last_y = y;
437 if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
438 kbd_mouse_event(dx, dy, 0, s->button_mask);
441 if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
442 GdkDrawable *drawable = GDK_DRAWABLE(gtk_widget_get_window(s->drawing_area));
443 GdkDisplay *display = gdk_drawable_get_display(drawable);
444 GdkScreen *screen = gdk_drawable_get_screen(drawable);
445 int x = (int)motion->x_root;
446 int y = (int)motion->y_root;
448 /* In relative mode check to see if client pointer hit
449 * one of the screen edges, and if so move it back by
450 * 200 pixels. This is important because the pointer
451 * in the server doesn't correspond 1-for-1, and so
452 * may still be only half way across the screen. Without
453 * this warp, the server pointer would thus appear to hit
454 * an invisible wall */
455 if (x == 0) {
456 x += 200;
458 if (y == 0) {
459 y += 200;
461 if (x == (gdk_screen_get_width(screen) - 1)) {
462 x -= 200;
464 if (y == (gdk_screen_get_height(screen) - 1)) {
465 y -= 200;
468 if (x != (int)motion->x_root || y != (int)motion->y_root) {
469 gdk_display_warp_pointer(display, screen, x, y);
470 s->last_x = -1;
471 s->last_y = -1;
472 return FALSE;
475 return TRUE;
478 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
479 void *opaque)
481 GtkDisplayState *s = opaque;
482 int dx, dy;
483 int n;
485 if (button->button == 1) {
486 n = 0x01;
487 } else if (button->button == 2) {
488 n = 0x04;
489 } else if (button->button == 3) {
490 n = 0x02;
491 } else {
492 n = 0x00;
495 if (button->type == GDK_BUTTON_PRESS) {
496 s->button_mask |= n;
497 } else if (button->type == GDK_BUTTON_RELEASE) {
498 s->button_mask &= ~n;
501 if (kbd_mouse_is_absolute()) {
502 dx = s->last_x * 0x7FFF / (ds_get_width(s->ds) - 1);
503 dy = s->last_y * 0x7FFF / (ds_get_height(s->ds) - 1);
504 } else {
505 dx = 0;
506 dy = 0;
509 kbd_mouse_event(dx, dy, 0, s->button_mask);
511 return TRUE;
514 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
516 int gdk_keycode;
517 int qemu_keycode;
519 gdk_keycode = key->hardware_keycode;
521 if (gdk_keycode < 9) {
522 qemu_keycode = 0;
523 } else if (gdk_keycode < 97) {
524 qemu_keycode = gdk_keycode - 8;
525 } else if (gdk_keycode < 158) {
526 qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
527 } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
528 qemu_keycode = 0x70;
529 } else if (gdk_keycode == 211) { /* backslash */
530 qemu_keycode = 0x73;
531 } else {
532 qemu_keycode = 0;
535 DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
536 gdk_keycode, qemu_keycode,
537 (key->type == GDK_KEY_PRESS) ? "down" : "up");
539 if (qemu_keycode & SCANCODE_GREY) {
540 kbd_put_keycode(SCANCODE_EMUL0);
543 if (key->type == GDK_KEY_PRESS) {
544 kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
545 } else if (key->type == GDK_KEY_RELEASE) {
546 kbd_put_keycode(qemu_keycode | SCANCODE_UP);
547 } else {
548 g_assert_not_reached();
551 return TRUE;
554 /** Window Menu Actions **/
556 static void gd_menu_quit(GtkMenuItem *item, void *opaque)
558 qmp_quit(NULL);
561 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
563 GtkDisplayState *s = opaque;
565 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
566 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
567 } else {
568 int i;
570 for (i = 0; i < s->nb_vcs; i++) {
571 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
572 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
573 break;
579 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
581 GtkDisplayState *s = opaque;
583 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
584 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
585 } else {
586 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
590 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
592 GtkDisplayState *s = opaque;
594 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->full_screen_item))) {
595 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
596 gtk_widget_set_size_request(s->menu_bar, 0, 0);
597 gtk_widget_set_size_request(s->drawing_area, -1, -1);
598 gtk_window_fullscreen(GTK_WINDOW(s->window));
599 if (gd_on_vga(s)) {
600 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
602 s->full_screen = TRUE;
603 } else {
604 gtk_window_unfullscreen(GTK_WINDOW(s->window));
605 gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
606 gtk_widget_set_size_request(s->menu_bar, -1, -1);
607 gtk_widget_set_size_request(s->drawing_area,
608 ds_get_width(s->ds), ds_get_height(s->ds));
609 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE);
610 s->full_screen = FALSE;
611 s->scale_x = 1.0;
612 s->scale_y = 1.0;
615 gd_update_cursor(s, FALSE);
618 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
620 GtkDisplayState *s = opaque;
622 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
623 FALSE);
625 s->scale_x += .25;
626 s->scale_y += .25;
628 gd_resize(s->ds);
631 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
633 GtkDisplayState *s = opaque;
635 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
636 FALSE);
638 s->scale_x -= .25;
639 s->scale_y -= .25;
641 s->scale_x = MAX(s->scale_x, .25);
642 s->scale_y = MAX(s->scale_y, .25);
644 gd_resize(s->ds);
647 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
649 GtkDisplayState *s = opaque;
651 s->scale_x = 1.0;
652 s->scale_y = 1.0;
654 gd_resize(s->ds);
657 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
659 GtkDisplayState *s = opaque;
660 int ww, wh;
662 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
663 s->free_scale = TRUE;
664 } else {
665 s->free_scale = FALSE;
668 gd_resize(s->ds);
670 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
671 gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh);
674 static void gd_grab_keyboard(GtkDisplayState *s)
676 gdk_keyboard_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
677 FALSE,
678 GDK_CURRENT_TIME);
681 static void gd_ungrab_keyboard(GtkDisplayState *s)
683 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
686 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
688 GtkDisplayState *s = opaque;
690 if (gd_is_grab_active(s)) {
691 gd_grab_keyboard(s);
692 gdk_pointer_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
693 FALSE, /* All events to come to our window directly */
694 GDK_POINTER_MOTION_MASK |
695 GDK_BUTTON_PRESS_MASK |
696 GDK_BUTTON_RELEASE_MASK |
697 GDK_BUTTON_MOTION_MASK |
698 GDK_SCROLL_MASK,
699 NULL, /* Allow cursor to move over entire desktop */
700 s->null_cursor,
701 GDK_CURRENT_TIME);
702 } else {
703 gd_ungrab_keyboard(s);
704 gdk_pointer_ungrab(GDK_CURRENT_TIME);
707 gd_update_caption(s);
708 gd_update_cursor(s, FALSE);
711 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
712 gpointer data)
714 GtkDisplayState *s = data;
715 guint last_page;
716 gboolean on_vga;
718 if (!gtk_widget_get_realized(s->notebook)) {
719 return;
722 last_page = gtk_notebook_get_current_page(nb);
724 if (last_page) {
725 gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
728 on_vga = arg2 == 0;
730 if (!on_vga) {
731 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
732 FALSE);
733 } else if (s->full_screen) {
734 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
735 TRUE);
738 if (arg2 == 0) {
739 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
740 } else {
741 VirtualConsole *vc = &s->vc[arg2 - 1];
742 VteTerminal *term = VTE_TERMINAL(vc->terminal);
743 int width, height;
745 width = 80 * vte_terminal_get_char_width(term);
746 height = 25 * vte_terminal_get_char_height(term);
748 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
749 gtk_widget_set_size_request(vc->terminal, width, height);
752 gtk_widget_set_sensitive(s->grab_item, on_vga);
754 gd_update_cursor(s, TRUE);
757 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
759 GtkDisplayState *s = data;
761 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
762 gd_grab_keyboard(s);
765 return TRUE;
768 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
770 GtkDisplayState *s = data;
772 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
773 gd_ungrab_keyboard(s);
776 return TRUE;
779 /** Virtual Console Callbacks **/
781 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
783 VirtualConsole *vc = chr->opaque;
785 return write(vc->fd, buf, len);
788 static int nb_vcs;
789 static CharDriverState *vcs[MAX_VCS];
791 static CharDriverState *gd_vc_handler(QemuOpts *opts)
793 CharDriverState *chr;
795 chr = g_malloc0(sizeof(*chr));
796 chr->chr_write = gd_vc_chr_write;
798 vcs[nb_vcs++] = chr;
800 return chr;
803 void early_gtk_display_init(void)
805 register_vc_handler(gd_vc_handler);
808 static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
810 VirtualConsole *vc = opaque;
811 uint8_t buffer[1024];
812 ssize_t len;
814 len = read(vc->fd, buffer, sizeof(buffer));
815 if (len <= 0) {
816 return FALSE;
819 qemu_chr_be_write(vc->chr, buffer, len);
821 return TRUE;
824 static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group)
826 const char *label;
827 char buffer[32];
828 char path[32];
829 VtePty *pty;
830 GIOChannel *chan;
831 GtkWidget *scrolled_window;
832 GtkAdjustment *vadjustment;
833 int master_fd, slave_fd, ret;
834 struct termios tty;
836 snprintf(buffer, sizeof(buffer), "vc%d", index);
837 snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
839 vc->chr = vcs[index];
841 if (vc->chr->label) {
842 label = vc->chr->label;
843 } else {
844 label = buffer;
847 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
848 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
849 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
850 gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
852 vc->terminal = vte_terminal_new();
854 ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
855 g_assert(ret != -1);
857 /* Set raw attributes on the pty. */
858 tcgetattr(slave_fd, &tty);
859 cfmakeraw(&tty);
860 tcsetattr(slave_fd, TCSAFLUSH, &tty);
862 pty = vte_pty_new_foreign(master_fd, NULL);
864 vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
866 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
868 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
870 scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
871 gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
873 vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
875 vc->fd = slave_fd;
876 vc->chr->opaque = vc;
877 vc->scrolled_window = scrolled_window;
879 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
880 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
882 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
883 g_signal_connect(vc->menu_item, "activate",
884 G_CALLBACK(gd_menu_switch_vc), s);
886 gtk_menu_append(GTK_MENU(s->view_menu), vc->menu_item);
888 qemu_chr_generic_open(vc->chr);
889 if (vc->chr->init) {
890 vc->chr->init(vc->chr);
893 chan = g_io_channel_unix_new(vc->fd);
894 g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
896 return group;
899 /** Window Creation **/
901 static void gd_connect_signals(GtkDisplayState *s)
903 g_signal_connect(s->show_tabs_item, "activate",
904 G_CALLBACK(gd_menu_show_tabs), s);
906 g_signal_connect(s->window, "delete-event",
907 G_CALLBACK(gd_window_close), s);
909 g_signal_connect(s->drawing_area, "expose-event",
910 G_CALLBACK(gd_expose_event), s);
911 g_signal_connect(s->drawing_area, "motion-notify-event",
912 G_CALLBACK(gd_motion_event), s);
913 g_signal_connect(s->drawing_area, "button-press-event",
914 G_CALLBACK(gd_button_event), s);
915 g_signal_connect(s->drawing_area, "button-release-event",
916 G_CALLBACK(gd_button_event), s);
917 g_signal_connect(s->drawing_area, "key-press-event",
918 G_CALLBACK(gd_key_event), s);
919 g_signal_connect(s->drawing_area, "key-release-event",
920 G_CALLBACK(gd_key_event), s);
922 g_signal_connect(s->quit_item, "activate",
923 G_CALLBACK(gd_menu_quit), s);
924 g_signal_connect(s->full_screen_item, "activate",
925 G_CALLBACK(gd_menu_full_screen), s);
926 g_signal_connect(s->zoom_in_item, "activate",
927 G_CALLBACK(gd_menu_zoom_in), s);
928 g_signal_connect(s->zoom_out_item, "activate",
929 G_CALLBACK(gd_menu_zoom_out), s);
930 g_signal_connect(s->zoom_fixed_item, "activate",
931 G_CALLBACK(gd_menu_zoom_fixed), s);
932 g_signal_connect(s->zoom_fit_item, "activate",
933 G_CALLBACK(gd_menu_zoom_fit), s);
934 g_signal_connect(s->vga_item, "activate",
935 G_CALLBACK(gd_menu_switch_vc), s);
936 g_signal_connect(s->grab_item, "activate",
937 G_CALLBACK(gd_menu_grab_input), s);
938 g_signal_connect(s->notebook, "switch-page",
939 G_CALLBACK(gd_change_page), s);
940 g_signal_connect(s->drawing_area, "enter-notify-event",
941 G_CALLBACK(gd_enter_event), s);
942 g_signal_connect(s->drawing_area, "leave-notify-event",
943 G_CALLBACK(gd_leave_event), s);
946 static void gd_create_menus(GtkDisplayState *s)
948 GtkStockItem item;
949 GtkAccelGroup *accel_group;
950 GSList *group = NULL;
951 GtkWidget *separator;
952 int i;
954 accel_group = gtk_accel_group_new();
955 s->file_menu = gtk_menu_new();
956 gtk_menu_set_accel_group(GTK_MENU(s->file_menu), accel_group);
957 s->file_menu_item = gtk_menu_item_new_with_mnemonic(_("_File"));
959 s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
960 gtk_stock_lookup(GTK_STOCK_QUIT, &item);
961 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
962 "<QEMU>/File/Quit");
963 gtk_accel_map_add_entry("<QEMU>/File/Quit", item.keyval, item.modifier);
965 s->view_menu = gtk_menu_new();
966 gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group);
967 s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
969 s->full_screen_item = gtk_check_menu_item_new_with_mnemonic(_("_Full Screen"));
970 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item),
971 "<QEMU>/View/Full Screen");
972 gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, GDK_CONTROL_MASK | GDK_MOD1_MASK);
973 gtk_menu_append(GTK_MENU(s->view_menu), s->full_screen_item);
975 separator = gtk_separator_menu_item_new();
976 gtk_menu_append(GTK_MENU(s->view_menu), separator);
978 s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL);
979 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
980 "<QEMU>/View/Zoom In");
981 gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
982 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_in_item);
984 s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL);
985 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
986 "<QEMU>/View/Zoom Out");
987 gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
988 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_out_item);
990 s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL);
991 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
992 "<QEMU>/View/Zoom Fixed");
993 gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, GDK_CONTROL_MASK | GDK_MOD1_MASK);
994 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_fixed_item);
996 s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
997 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_fit_item);
999 separator = gtk_separator_menu_item_new();
1000 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1002 s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1003 gtk_menu_append(GTK_MENU(s->view_menu), s->grab_on_hover_item);
1005 s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
1006 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
1007 "<QEMU>/View/Grab Input");
1008 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1009 gtk_menu_append(GTK_MENU(s->view_menu), s->grab_item);
1011 separator = gtk_separator_menu_item_new();
1012 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1014 s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
1015 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
1016 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
1017 "<QEMU>/View/VGA");
1018 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1019 gtk_menu_append(GTK_MENU(s->view_menu), s->vga_item);
1021 for (i = 0; i < nb_vcs; i++) {
1022 VirtualConsole *vc = &s->vc[i];
1024 group = gd_vc_init(s, vc, i, group);
1025 s->nb_vcs++;
1028 separator = gtk_separator_menu_item_new();
1029 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1031 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1032 gtk_menu_append(GTK_MENU(s->view_menu), s->show_tabs_item);
1034 g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
1035 gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
1037 gtk_menu_append(GTK_MENU(s->file_menu), s->quit_item);
1038 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->file_menu_item), s->file_menu);
1039 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->file_menu_item);
1041 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1042 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1045 void gtk_display_init(DisplayState *ds)
1047 GtkDisplayState *s = g_malloc0(sizeof(*s));
1049 gtk_init(NULL, NULL);
1051 ds->opaque = s;
1052 s->ds = ds;
1053 s->dcl.dpy_gfx_update = gd_update;
1054 s->dcl.dpy_gfx_resize = gd_resize;
1055 s->dcl.dpy_refresh = gd_refresh;
1057 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1058 s->vbox = gtk_vbox_new(FALSE, 0);
1059 s->notebook = gtk_notebook_new();
1060 s->drawing_area = gtk_drawing_area_new();
1061 s->menu_bar = gtk_menu_bar_new();
1063 s->scale_x = 1.0;
1064 s->scale_y = 1.0;
1065 s->free_scale = FALSE;
1067 setlocale(LC_ALL, "");
1068 bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1069 textdomain("qemu");
1071 s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1073 s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1074 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1075 qemu_add_vm_change_state_handler(gd_change_runstate, s);
1077 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
1079 gd_create_menus(s);
1081 gd_connect_signals(s);
1083 gtk_widget_add_events(s->drawing_area,
1084 GDK_POINTER_MOTION_MASK |
1085 GDK_BUTTON_PRESS_MASK |
1086 GDK_BUTTON_RELEASE_MASK |
1087 GDK_BUTTON_MOTION_MASK |
1088 GDK_ENTER_NOTIFY_MASK |
1089 GDK_LEAVE_NOTIFY_MASK |
1090 GDK_SCROLL_MASK |
1091 GDK_KEY_PRESS_MASK);
1092 gtk_widget_set_double_buffered(s->drawing_area, FALSE);
1093 gtk_widget_set_can_focus(s->drawing_area, TRUE);
1095 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1096 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1098 gd_update_caption(s);
1100 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1101 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1103 gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
1105 gtk_widget_show_all(s->window);
1107 register_displaychangelistener(ds, &s->dcl);
1109 global_state = s;