qemu/ui/gtk.c
<<
>>
Prefs
   1/*
   2 * GTK UI
   3 *
   4 * Copyright IBM, Corp. 2012
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   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.
  11 *
  12 * Portions from gtk-vnc:
  13 *
  14 * GTK VNC Widget
  15 *
  16 * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
  17 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
  18 *
  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.
  23 *
  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.
  28 *
  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
  32 */
  33
  34#define GETTEXT_PACKAGE "qemu"
  35#define LOCALEDIR "po"
  36
  37#include "qemu-common.h"
  38
  39#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
  40/* Work around an -Wstrict-prototypes warning in GTK headers */
  41#pragma GCC diagnostic push
  42#pragma GCC diagnostic ignored "-Wstrict-prototypes"
  43#endif
  44#include <gtk/gtk.h>
  45#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
  46#pragma GCC diagnostic pop
  47#endif
  48
  49
  50#include <gdk/gdkkeysyms.h>
  51#include <glib/gi18n.h>
  52#include <locale.h>
  53#include <vte/vte.h>
  54#include <sys/types.h>
  55#include <sys/socket.h>
  56#include <sys/un.h>
  57#include <sys/wait.h>
  58#include <math.h>
  59
  60#include "ui/console.h"
  61#include "sysemu/sysemu.h"
  62#include "qmp-commands.h"
  63#include "x_keymap.h"
  64#include "keymaps.h"
  65#include "sysemu/char.h"
  66
  67//#define DEBUG_GTK
  68
  69#ifdef DEBUG_GTK
  70#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
  71#else
  72#define DPRINTF(fmt, ...) do { } while (0)
  73#endif
  74
  75#define MAX_VCS 10
  76
  77
  78/* Compatibility define to let us build on both Gtk2 and Gtk3 */
  79#if GTK_CHECK_VERSION(3, 0, 0)
  80static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
  81{
  82    *ww = gdk_window_get_width(w);
  83    *wh = gdk_window_get_height(w);
  84}
  85#endif
  86
  87#if !GTK_CHECK_VERSION(2, 20, 0)
  88#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
  89#endif
  90
  91#ifndef GDK_KEY_0
  92#define GDK_KEY_0 GDK_0
  93#define GDK_KEY_1 GDK_1
  94#define GDK_KEY_2 GDK_2
  95#define GDK_KEY_f GDK_f
  96#define GDK_KEY_g GDK_g
  97#define GDK_KEY_plus GDK_plus
  98#define GDK_KEY_minus GDK_minus
  99#endif
 100
 101static const int modifier_keycode[] = {
 102    /* shift, control, alt keys, meta keys, both left & right */
 103    0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
 104};
 105
 106typedef struct VirtualConsole
 107{
 108    GtkWidget *menu_item;
 109    GtkWidget *terminal;
 110    GtkWidget *scrolled_window;
 111    CharDriverState *chr;
 112    int fd;
 113} VirtualConsole;
 114
 115typedef struct GtkDisplayState
 116{
 117    GtkWidget *window;
 118
 119    GtkWidget *menu_bar;
 120
 121    GtkAccelGroup *accel_group;
 122
 123    GtkWidget *machine_menu_item;
 124    GtkWidget *machine_menu;
 125    GtkWidget *pause_item;
 126    GtkWidget *reset_item;
 127    GtkWidget *powerdown_item;
 128    GtkWidget *quit_item;
 129
 130    GtkWidget *view_menu_item;
 131    GtkWidget *view_menu;
 132    GtkWidget *full_screen_item;
 133    GtkWidget *zoom_in_item;
 134    GtkWidget *zoom_out_item;
 135    GtkWidget *zoom_fixed_item;
 136    GtkWidget *zoom_fit_item;
 137    GtkWidget *grab_item;
 138    GtkWidget *grab_on_hover_item;
 139    GtkWidget *vga_item;
 140
 141    int nb_vcs;
 142    VirtualConsole vc[MAX_VCS];
 143
 144    GtkWidget *show_tabs_item;
 145
 146    GtkWidget *vbox;
 147    GtkWidget *notebook;
 148    GtkWidget *drawing_area;
 149    cairo_surface_t *surface;
 150    DisplayChangeListener dcl;
 151    DisplaySurface *ds;
 152    int button_mask;
 153    int last_x;
 154    int last_y;
 155
 156    double scale_x;
 157    double scale_y;
 158    gboolean full_screen;
 159
 160    GdkCursor *null_cursor;
 161    Notifier mouse_mode_notifier;
 162    gboolean free_scale;
 163
 164    bool external_pause_update;
 165
 166    bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
 167} GtkDisplayState;
 168
 169static GtkDisplayState *global_state;
 170
 171/** Utility Functions **/
 172
 173static bool gd_is_grab_active(GtkDisplayState *s)
 174{
 175    return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
 176}
 177
 178static bool gd_grab_on_hover(GtkDisplayState *s)
 179{
 180    return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
 181}
 182
 183static bool gd_on_vga(GtkDisplayState *s)
 184{
 185    return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
 186}
 187
 188static void gd_update_cursor(GtkDisplayState *s, gboolean override)
 189{
 190    GdkWindow *window;
 191    bool on_vga;
 192
 193    window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
 194
 195    on_vga = gd_on_vga(s);
 196
 197    if ((override || on_vga) &&
 198        (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
 199        gdk_window_set_cursor(window, s->null_cursor);
 200    } else {
 201        gdk_window_set_cursor(window, NULL);
 202    }
 203}
 204
 205static void gd_update_caption(GtkDisplayState *s)
 206{
 207    const char *status = "";
 208    gchar *title;
 209    const char *grab = "";
 210    bool is_paused = !runstate_is_running();
 211
 212    if (gd_is_grab_active(s)) {
 213        grab = _(" - Press Ctrl+Alt+G to release grab");
 214    }
 215
 216    if (is_paused) {
 217        status = _(" [Paused]");
 218    }
 219    s->external_pause_update = true;
 220    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
 221                                   is_paused);
 222    s->external_pause_update = false;
 223
 224    if (qemu_name) {
 225        title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
 226    } else {
 227        title = g_strdup_printf("QEMU%s%s", status, grab);
 228    }
 229
 230    gtk_window_set_title(GTK_WINDOW(s->window), title);
 231
 232    g_free(title);
 233}
 234
 235static void gd_update_windowsize(GtkDisplayState *s)
 236{
 237    if (!s->full_screen) {
 238        GtkRequisition req;
 239        double sx, sy;
 240
 241        if (s->free_scale) {
 242            sx = s->scale_x;
 243            sy = s->scale_y;
 244
 245            s->scale_y = 1.0;
 246            s->scale_x = 1.0;
 247        } else {
 248            sx = 1.0;
 249            sy = 1.0;
 250        }
 251
 252        gtk_widget_set_size_request(s->drawing_area,
 253                                    surface_width(s->ds) * s->scale_x,
 254                                    surface_height(s->ds) * s->scale_y);
 255#if GTK_CHECK_VERSION(3, 0, 0)
 256        gtk_widget_get_preferred_size(s->vbox, NULL, &req);
 257#else
 258        gtk_widget_size_request(s->vbox, &req);
 259#endif
 260
 261        gtk_window_resize(GTK_WINDOW(s->window),
 262                          req.width * sx, req.height * sy);
 263    }
 264}
 265
 266static void gd_update_full_redraw(GtkDisplayState *s)
 267{
 268    int ww, wh;
 269    gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
 270    gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh);
 271}
 272
 273static void gtk_release_modifiers(GtkDisplayState *s)
 274{
 275    int i, keycode;
 276
 277    if (!gd_on_vga(s)) {
 278        return;
 279    }
 280    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
 281        keycode = modifier_keycode[i];
 282        if (!s->modifier_pressed[i]) {
 283            continue;
 284        }
 285        if (keycode & SCANCODE_GREY) {
 286            kbd_put_keycode(SCANCODE_EMUL0);
 287        }
 288        kbd_put_keycode(keycode | SCANCODE_UP);
 289        s->modifier_pressed[i] = false;
 290    }
 291}
 292
 293/** DisplayState Callbacks **/
 294
 295static void gd_update(DisplayChangeListener *dcl,
 296                      int x, int y, int w, int h)
 297{
 298    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
 299    int x1, x2, y1, y2;
 300    int mx, my;
 301    int fbw, fbh;
 302    int ww, wh;
 303
 304    DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
 305
 306    x1 = floor(x * s->scale_x);
 307    y1 = floor(y * s->scale_y);
 308
 309    x2 = ceil(x * s->scale_x + w * s->scale_x);
 310    y2 = ceil(y * s->scale_y + h * s->scale_y);
 311
 312    fbw = surface_width(s->ds) * s->scale_x;
 313    fbh = surface_height(s->ds) * s->scale_y;
 314
 315    gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
 316
 317    mx = my = 0;
 318    if (ww > fbw) {
 319        mx = (ww - fbw) / 2;
 320    }
 321    if (wh > fbh) {
 322        my = (wh - fbh) / 2;
 323    }
 324
 325    gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1));
 326}
 327
 328static void gd_refresh(DisplayChangeListener *dcl)
 329{
 330    graphic_hw_update(dcl->con);
 331}
 332
 333#if GTK_CHECK_VERSION(3, 0, 0)
 334static void gd_mouse_set(DisplayChangeListener *dcl,
 335                         int x, int y, int visible)
 336{
 337    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
 338    GdkDisplay *dpy;
 339    GdkDeviceManager *mgr;
 340    gint x_root, y_root;
 341
 342    dpy = gtk_widget_get_display(s->drawing_area);
 343    mgr = gdk_display_get_device_manager(dpy);
 344    gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
 345                               x, y, &x_root, &y_root);
 346    gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
 347                    gtk_widget_get_screen(s->drawing_area),
 348                    x, y);
 349}
 350#else
 351static void gd_mouse_set(DisplayChangeListener *dcl,
 352                         int x, int y, int visible)
 353{
 354    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
 355    gint x_root, y_root;
 356
 357    gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
 358                               x, y, &x_root, &y_root);
 359    gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area),
 360                             gtk_widget_get_screen(s->drawing_area),
 361                             x_root, y_root);
 362}
 363#endif
 364
 365static void gd_cursor_define(DisplayChangeListener *dcl,
 366                             QEMUCursor *c)
 367{
 368    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
 369    GdkPixbuf *pixbuf;
 370    GdkCursor *cursor;
 371
 372    pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
 373                                      GDK_COLORSPACE_RGB, true, 8,
 374                                      c->width, c->height, c->width * 4,
 375                                      NULL, NULL);
 376    cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area),
 377                                        pixbuf, c->hot_x, c->hot_y);
 378    gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor);
 379    g_object_unref(pixbuf);
 380    g_object_unref(cursor);
 381}
 382
 383static void gd_switch(DisplayChangeListener *dcl,
 384                      DisplaySurface *surface)
 385{
 386    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
 387    cairo_format_t kind;
 388    bool resized = true;
 389    int stride;
 390
 391    DPRINTF("resize(width=%d, height=%d)\n",
 392            surface_width(surface), surface_height(surface));
 393
 394    if (s->surface) {
 395        cairo_surface_destroy(s->surface);
 396    }
 397
 398    if (s->ds &&
 399        surface_width(s->ds) == surface_width(surface) &&
 400        surface_height(s->ds) == surface_height(surface)) {
 401        resized = false;
 402    }
 403    s->ds = surface;
 404    switch (surface_bits_per_pixel(surface)) {
 405    case 8:
 406        kind = CAIRO_FORMAT_A8;
 407        break;
 408    case 16:
 409        kind = CAIRO_FORMAT_RGB16_565;
 410        break;
 411    case 32:
 412        kind = CAIRO_FORMAT_RGB24;
 413        break;
 414    default:
 415        g_assert_not_reached();
 416        break;
 417    }
 418
 419    stride = cairo_format_stride_for_width(kind, surface_width(surface));
 420    g_assert(surface_stride(surface) == stride);
 421
 422    s->surface = cairo_image_surface_create_for_data(surface_data(surface),
 423                                                     kind,
 424                                                     surface_width(surface),
 425                                                     surface_height(surface),
 426                                                     surface_stride(surface));
 427
 428    if (resized) {
 429        gd_update_windowsize(s);
 430    } else {
 431        gd_update_full_redraw(s);
 432    }
 433}
 434
 435/** QEMU Events **/
 436
 437static void gd_change_runstate(void *opaque, int running, RunState state)
 438{
 439    GtkDisplayState *s = opaque;
 440
 441    gd_update_caption(s);
 442}
 443
 444static void gd_mouse_mode_change(Notifier *notify, void *data)
 445{
 446    gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
 447                     FALSE);
 448}
 449
 450/** GTK Events **/
 451
 452static gboolean gd_window_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
 453{
 454    GtkDisplayState *s = opaque;
 455    GtkAccelGroupEntry *entries;
 456    guint n_entries = 0;
 457    gboolean propagate_accel = TRUE;
 458    gboolean handled = FALSE;
 459
 460    entries = gtk_accel_group_query(s->accel_group, key->keyval,
 461                                    key->state, &n_entries);
 462    if (n_entries) {
 463        const char *quark = g_quark_to_string(entries[0].accel_path_quark);
 464
 465        if (gd_is_grab_active(s) && strstart(quark, "<QEMU>/File/", NULL)) {
 466            propagate_accel = FALSE;
 467        }
 468    }
 469
 470    if (!handled && propagate_accel) {
 471        handled = gtk_window_activate_key(GTK_WINDOW(widget), key);
 472    }
 473    if (handled) {
 474        gtk_release_modifiers(s);
 475    } else {
 476        handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), key);
 477    }
 478
 479    return handled;
 480}
 481
 482static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
 483                                void *opaque)
 484{
 485    GtkDisplayState *s = opaque;
 486
 487    if (!no_quit) {
 488        unregister_displaychangelistener(&s->dcl);
 489        qmp_quit(NULL);
 490        return FALSE;
 491    }
 492
 493    return TRUE;
 494}
 495
 496static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
 497{
 498    GtkDisplayState *s = opaque;
 499    int mx, my;
 500    int ww, wh;
 501    int fbw, fbh;
 502
 503    if (!gtk_widget_get_realized(widget)) {
 504        return FALSE;
 505    }
 506
 507    fbw = surface_width(s->ds);
 508    fbh = surface_height(s->ds);
 509
 510    gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
 511
 512    if (s->full_screen) {
 513        s->scale_x = (double)ww / fbw;
 514        s->scale_y = (double)wh / fbh;
 515    } else if (s->free_scale) {
 516        double sx, sy;
 517
 518        sx = (double)ww / fbw;
 519        sy = (double)wh / fbh;
 520
 521        s->scale_x = s->scale_y = MIN(sx, sy);
 522    }
 523
 524    fbw *= s->scale_x;
 525    fbh *= s->scale_y;
 526
 527    mx = my = 0;
 528    if (ww > fbw) {
 529        mx = (ww - fbw) / 2;
 530    }
 531    if (wh > fbh) {
 532        my = (wh - fbh) / 2;
 533    }
 534
 535    cairo_rectangle(cr, 0, 0, ww, wh);
 536
 537    /* Optionally cut out the inner area where the pixmap
 538       will be drawn. This avoids 'flashing' since we're
 539       not double-buffering. Note we're using the undocumented
 540       behaviour of drawing the rectangle from right to left
 541       to cut out the whole */
 542    cairo_rectangle(cr, mx + fbw, my,
 543                    -1 * fbw, fbh);
 544    cairo_fill(cr);
 545
 546    cairo_scale(cr, s->scale_x, s->scale_y);
 547    cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y);
 548    cairo_paint(cr);
 549
 550    return TRUE;
 551}
 552
 553#if !GTK_CHECK_VERSION(3, 0, 0)
 554static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
 555                                void *opaque)
 556{
 557    cairo_t *cr;
 558    gboolean ret;
 559
 560    cr = gdk_cairo_create(gtk_widget_get_window(widget));
 561    cairo_rectangle(cr,
 562                    expose->area.x,
 563                    expose->area.y,
 564                    expose->area.width,
 565                    expose->area.height);
 566    cairo_clip(cr);
 567
 568    ret = gd_draw_event(widget, cr, opaque);
 569
 570    cairo_destroy(cr);
 571
 572    return ret;
 573}
 574#endif
 575
 576static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
 577                                void *opaque)
 578{
 579    GtkDisplayState *s = opaque;
 580    int dx, dy;
 581    int x, y;
 582    int mx, my;
 583    int fbh, fbw;
 584    int ww, wh;
 585
 586    fbw = surface_width(s->ds) * s->scale_x;
 587    fbh = surface_height(s->ds) * s->scale_y;
 588
 589    gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
 590
 591    mx = my = 0;
 592    if (ww > fbw) {
 593        mx = (ww - fbw) / 2;
 594    }
 595    if (wh > fbh) {
 596        my = (wh - fbh) / 2;
 597    }
 598
 599    x = (motion->x - mx) / s->scale_x;
 600    y = (motion->y - my) / s->scale_y;
 601
 602    if (x < 0 || y < 0 ||
 603        x >= surface_width(s->ds) ||
 604        y >= surface_height(s->ds)) {
 605        return TRUE;
 606    }
 607
 608    if (kbd_mouse_is_absolute()) {
 609        dx = x * 0x7FFF / (surface_width(s->ds) - 1);
 610        dy = y * 0x7FFF / (surface_height(s->ds) - 1);
 611    } else if (s->last_x == -1 || s->last_y == -1) {
 612        dx = 0;
 613        dy = 0;
 614    } else {
 615        dx = x - s->last_x;
 616        dy = y - s->last_y;
 617    }
 618
 619    s->last_x = x;
 620    s->last_y = y;
 621
 622    if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
 623        kbd_mouse_event(dx, dy, 0, s->button_mask);
 624    }
 625
 626    if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
 627        GdkScreen *screen = gtk_widget_get_screen(s->drawing_area);
 628        int x = (int)motion->x_root;
 629        int y = (int)motion->y_root;
 630
 631        /* In relative mode check to see if client pointer hit
 632         * one of the screen edges, and if so move it back by
 633         * 200 pixels. This is important because the pointer
 634         * in the server doesn't correspond 1-for-1, and so
 635         * may still be only half way across the screen. Without
 636         * this warp, the server pointer would thus appear to hit
 637         * an invisible wall */
 638        if (x == 0) {
 639            x += 200;
 640        }
 641        if (y == 0) {
 642            y += 200;
 643        }
 644        if (x == (gdk_screen_get_width(screen) - 1)) {
 645            x -= 200;
 646        }
 647        if (y == (gdk_screen_get_height(screen) - 1)) {
 648            y -= 200;
 649        }
 650
 651        if (x != (int)motion->x_root || y != (int)motion->y_root) {
 652#if GTK_CHECK_VERSION(3, 0, 0)
 653            GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
 654            gdk_device_warp(dev, screen, x, y);
 655#else
 656            GdkDisplay *display = gtk_widget_get_display(widget);
 657            gdk_display_warp_pointer(display, screen, x, y);
 658#endif
 659            s->last_x = -1;
 660            s->last_y = -1;
 661            return FALSE;
 662        }
 663    }
 664    return TRUE;
 665}
 666
 667static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
 668                                void *opaque)
 669{
 670    GtkDisplayState *s = opaque;
 671    int dx, dy;
 672    int n;
 673
 674    if (button->button == 1) {
 675        n = 0x01;
 676    } else if (button->button == 2) {
 677        n = 0x04;
 678    } else if (button->button == 3) {
 679        n = 0x02;
 680    } else {
 681        n = 0x00;
 682    }
 683
 684    if (button->type == GDK_BUTTON_PRESS) {
 685        s->button_mask |= n;
 686    } else if (button->type == GDK_BUTTON_RELEASE) {
 687        s->button_mask &= ~n;
 688    }
 689
 690    if (kbd_mouse_is_absolute()) {
 691        dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1);
 692        dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1);
 693    } else {
 694        dx = 0;
 695        dy = 0;
 696    }
 697
 698    kbd_mouse_event(dx, dy, 0, s->button_mask);
 699        
 700    return TRUE;
 701}
 702
 703static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
 704{
 705    GtkDisplayState *s = opaque;
 706    int gdk_keycode;
 707    int qemu_keycode;
 708    int i;
 709
 710    gdk_keycode = key->hardware_keycode;
 711
 712    if (gdk_keycode < 9) {
 713        qemu_keycode = 0;
 714    } else if (gdk_keycode < 97) {
 715        qemu_keycode = gdk_keycode - 8;
 716    } else if (gdk_keycode < 158) {
 717        qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
 718    } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
 719        qemu_keycode = 0x70;
 720    } else if (gdk_keycode == 211) { /* backslash */
 721        qemu_keycode = 0x73;
 722    } else {
 723        qemu_keycode = 0;
 724    }
 725
 726    DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
 727            gdk_keycode, qemu_keycode,
 728            (key->type == GDK_KEY_PRESS) ? "down" : "up");
 729
 730    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
 731        if (qemu_keycode == modifier_keycode[i]) {
 732            s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
 733        }
 734    }
 735
 736    if (qemu_keycode & SCANCODE_GREY) {
 737        kbd_put_keycode(SCANCODE_EMUL0);
 738    }
 739
 740    if (key->type == GDK_KEY_PRESS) {
 741        kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
 742    } else if (key->type == GDK_KEY_RELEASE) {
 743        kbd_put_keycode(qemu_keycode | SCANCODE_UP);
 744    } else {
 745        g_assert_not_reached();
 746    }
 747
 748    return TRUE;
 749}
 750
 751/** Window Menu Actions **/
 752
 753static void gd_menu_pause(GtkMenuItem *item, void *opaque)
 754{
 755    GtkDisplayState *s = opaque;
 756
 757    if (s->external_pause_update) {
 758        return;
 759    }
 760    if (runstate_is_running()) {
 761        qmp_stop(NULL);
 762    } else {
 763        qmp_cont(NULL);
 764    }
 765}
 766
 767static void gd_menu_reset(GtkMenuItem *item, void *opaque)
 768{
 769    qmp_system_reset(NULL);
 770}
 771
 772static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
 773{
 774    qmp_system_powerdown(NULL);
 775}
 776
 777static void gd_menu_quit(GtkMenuItem *item, void *opaque)
 778{
 779    qmp_quit(NULL);
 780}
 781
 782static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
 783{
 784    GtkDisplayState *s = opaque;
 785
 786    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
 787        gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
 788    } else {
 789        int i;
 790
 791        gtk_release_modifiers(s);
 792        for (i = 0; i < s->nb_vcs; i++) {
 793            if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
 794                gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
 795                break;
 796            }
 797        }
 798    }
 799}
 800
 801static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
 802{
 803    GtkDisplayState *s = opaque;
 804
 805    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
 806        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
 807    } else {
 808        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
 809    }
 810}
 811
 812static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
 813{
 814    GtkDisplayState *s = opaque;
 815
 816    if (!s->full_screen) {
 817        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
 818        gtk_widget_set_size_request(s->menu_bar, 0, 0);
 819        gtk_widget_set_size_request(s->drawing_area, -1, -1);
 820        gtk_window_fullscreen(GTK_WINDOW(s->window));
 821        if (gd_on_vga(s)) {
 822            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
 823        }
 824        s->full_screen = TRUE;
 825    } else {
 826        gtk_window_unfullscreen(GTK_WINDOW(s->window));
 827        gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
 828        gtk_widget_set_size_request(s->menu_bar, -1, -1);
 829        gtk_widget_set_size_request(s->drawing_area,
 830                                    surface_width(s->ds),
 831                                    surface_height(s->ds));
 832        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE);
 833        s->full_screen = FALSE;
 834        s->scale_x = 1.0;
 835        s->scale_y = 1.0;
 836    }
 837
 838    gd_update_cursor(s, FALSE);
 839}
 840
 841static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
 842{
 843    GtkDisplayState *s = opaque;
 844
 845    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
 846                                   FALSE);
 847
 848    s->scale_x += .25;
 849    s->scale_y += .25;
 850
 851    gd_update_windowsize(s);
 852}
 853
 854static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
 855{
 856    GtkDisplayState *s = opaque;
 857
 858    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
 859                                   FALSE);
 860
 861    s->scale_x -= .25;
 862    s->scale_y -= .25;
 863
 864    s->scale_x = MAX(s->scale_x, .25);
 865    s->scale_y = MAX(s->scale_y, .25);
 866
 867    gd_update_windowsize(s);
 868}
 869
 870static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
 871{
 872    GtkDisplayState *s = opaque;
 873
 874    s->scale_x = 1.0;
 875    s->scale_y = 1.0;
 876
 877    gd_update_windowsize(s);
 878}
 879
 880static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
 881{
 882    GtkDisplayState *s = opaque;
 883
 884    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
 885        s->free_scale = TRUE;
 886    } else {
 887        s->free_scale = FALSE;
 888    }
 889
 890    gd_update_windowsize(s);
 891    gd_update_full_redraw(s);
 892}
 893
 894static void gd_grab_keyboard(GtkDisplayState *s)
 895{
 896#if GTK_CHECK_VERSION(3, 0, 0)
 897    GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
 898    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
 899    GList *devices = gdk_device_manager_list_devices(mgr,
 900                                                     GDK_DEVICE_TYPE_MASTER);
 901    GList *tmp = devices;
 902    while (tmp) {
 903        GdkDevice *dev = tmp->data;
 904        if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
 905            gdk_device_grab(dev,
 906                            gtk_widget_get_window(s->drawing_area),
 907                            GDK_OWNERSHIP_NONE,
 908                            FALSE,
 909                            GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
 910                            NULL,
 911                            GDK_CURRENT_TIME);
 912        }
 913        tmp = tmp->next;
 914    }
 915    g_list_free(devices);
 916#else
 917    gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area),
 918                      FALSE,
 919                      GDK_CURRENT_TIME);
 920#endif
 921}
 922
 923static void gd_ungrab_keyboard(GtkDisplayState *s)
 924{
 925#if GTK_CHECK_VERSION(3, 0, 0)
 926    GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
 927    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
 928    GList *devices = gdk_device_manager_list_devices(mgr,
 929                                                     GDK_DEVICE_TYPE_MASTER);
 930    GList *tmp = devices;
 931    while (tmp) {
 932        GdkDevice *dev = tmp->data;
 933        if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
 934            gdk_device_ungrab(dev,
 935                              GDK_CURRENT_TIME);
 936        }
 937        tmp = tmp->next;
 938    }
 939    g_list_free(devices);
 940#else
 941    gdk_keyboard_ungrab(GDK_CURRENT_TIME);
 942#endif
 943}
 944
 945static void gd_grab_pointer(GtkDisplayState *s)
 946{
 947#if GTK_CHECK_VERSION(3, 0, 0)
 948    GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
 949    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
 950    GList *devices = gdk_device_manager_list_devices(mgr,
 951                                                     GDK_DEVICE_TYPE_MASTER);
 952    GList *tmp = devices;
 953    while (tmp) {
 954        GdkDevice *dev = tmp->data;
 955        if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
 956            gdk_device_grab(dev,
 957                            gtk_widget_get_window(s->drawing_area),
 958                            GDK_OWNERSHIP_NONE,
 959                            FALSE, /* All events to come to our
 960                                      window directly */
 961                            GDK_POINTER_MOTION_MASK |
 962                            GDK_BUTTON_PRESS_MASK |
 963                            GDK_BUTTON_RELEASE_MASK |
 964                            GDK_BUTTON_MOTION_MASK |
 965                            GDK_SCROLL_MASK,
 966                            s->null_cursor,
 967                            GDK_CURRENT_TIME);
 968        }
 969        tmp = tmp->next;
 970    }
 971    g_list_free(devices);
 972#else
 973    gdk_pointer_grab(gtk_widget_get_window(s->drawing_area),
 974                     FALSE, /* All events to come to our window directly */
 975                     GDK_POINTER_MOTION_MASK |
 976                     GDK_BUTTON_PRESS_MASK |
 977                     GDK_BUTTON_RELEASE_MASK |
 978                     GDK_BUTTON_MOTION_MASK |
 979                     GDK_SCROLL_MASK,
 980                     NULL, /* Allow cursor to move over entire desktop */
 981                     s->null_cursor,
 982                     GDK_CURRENT_TIME);
 983#endif
 984}
 985
 986static void gd_ungrab_pointer(GtkDisplayState *s)
 987{
 988#if GTK_CHECK_VERSION(3, 0, 0)
 989    GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
 990    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
 991    GList *devices = gdk_device_manager_list_devices(mgr,
 992                                                     GDK_DEVICE_TYPE_MASTER);
 993    GList *tmp = devices;
 994    while (tmp) {
 995        GdkDevice *dev = tmp->data;
 996        if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
 997            gdk_device_ungrab(dev,
 998                              GDK_CURRENT_TIME);
 999        }
1000        tmp = tmp->next;
1001    }
1002    g_list_free(devices);
1003#else
1004    gdk_pointer_ungrab(GDK_CURRENT_TIME);
1005#endif
1006}
1007
1008static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
1009{
1010    GtkDisplayState *s = opaque;
1011
1012    if (gd_is_grab_active(s)) {
1013        gd_grab_keyboard(s);
1014        gd_grab_pointer(s);
1015    } else {
1016        gd_ungrab_keyboard(s);
1017        gd_ungrab_pointer(s);
1018    }
1019
1020    gd_update_caption(s);
1021    gd_update_cursor(s, FALSE);
1022}
1023
1024static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1025                           gpointer data)
1026{
1027    GtkDisplayState *s = data;
1028    guint last_page;
1029    gboolean on_vga;
1030
1031    if (!gtk_widget_get_realized(s->notebook)) {
1032        return;
1033    }
1034
1035    last_page = gtk_notebook_get_current_page(nb);
1036
1037    if (last_page) {
1038        gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
1039    }
1040
1041    on_vga = arg2 == 0;
1042
1043    if (!on_vga) {
1044        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1045                                       FALSE);
1046    } else if (s->full_screen) {
1047        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1048                                       TRUE);
1049    }
1050
1051    if (arg2 == 0) {
1052        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
1053    } else {
1054        VirtualConsole *vc = &s->vc[arg2 - 1];
1055        VteTerminal *term = VTE_TERMINAL(vc->terminal);
1056        int width, height;
1057
1058        width = 80 * vte_terminal_get_char_width(term);
1059        height = 25 * vte_terminal_get_char_height(term);
1060
1061        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1062        gtk_widget_set_size_request(vc->terminal, width, height);
1063    }
1064
1065    gtk_widget_set_sensitive(s->grab_item, on_vga);
1066
1067    gd_update_cursor(s, TRUE);
1068}
1069
1070static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
1071{
1072    GtkDisplayState *s = data;
1073
1074    if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
1075        gd_grab_keyboard(s);
1076    }
1077
1078    return TRUE;
1079}
1080
1081static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
1082{
1083    GtkDisplayState *s = data;
1084
1085    if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
1086        gd_ungrab_keyboard(s);
1087    }
1088
1089    return TRUE;
1090}
1091
1092static gboolean gd_focus_out_event(GtkWidget *widget,
1093                                   GdkEventCrossing *crossing, gpointer data)
1094{
1095    GtkDisplayState *s = data;
1096
1097    gtk_release_modifiers(s);
1098
1099    return TRUE;
1100}
1101
1102/** Virtual Console Callbacks **/
1103
1104static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1105{
1106    VirtualConsole *vc = chr->opaque;
1107
1108    return write(vc->fd, buf, len);
1109}
1110
1111static int nb_vcs;
1112static CharDriverState *vcs[MAX_VCS];
1113
1114static CharDriverState *gd_vc_handler(ChardevVC *unused)
1115{
1116    CharDriverState *chr;
1117
1118    chr = g_malloc0(sizeof(*chr));
1119    chr->chr_write = gd_vc_chr_write;
1120    /* defer OPENED events until our vc is fully initialized */
1121    chr->explicit_be_open = true;
1122
1123    vcs[nb_vcs++] = chr;
1124
1125    return chr;
1126}
1127
1128void early_gtk_display_init(void)
1129{
1130    register_vc_handler(gd_vc_handler);
1131}
1132
1133static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
1134{
1135    VirtualConsole *vc = opaque;
1136    uint8_t buffer[1024];
1137    ssize_t len;
1138
1139    len = read(vc->fd, buffer, sizeof(buffer));
1140    if (len <= 0) {
1141        return FALSE;
1142    }
1143
1144    qemu_chr_be_write(vc->chr, buffer, len);
1145
1146    return TRUE;
1147}
1148
1149static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group,
1150                          GtkWidget *view_menu)
1151{
1152    const char *label;
1153    char buffer[32];
1154    char path[32];
1155#if VTE_CHECK_VERSION(0, 26, 0)
1156    VtePty *pty;
1157#endif
1158    GIOChannel *chan;
1159    GtkWidget *scrolled_window;
1160    GtkAdjustment *vadjustment;
1161    int master_fd, slave_fd;
1162
1163    snprintf(buffer, sizeof(buffer), "vc%d", index);
1164    snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
1165
1166    vc->chr = vcs[index];
1167
1168    if (vc->chr->label) {
1169        label = vc->chr->label;
1170    } else {
1171        label = buffer;
1172    }
1173
1174    vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
1175    group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1176    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
1177    gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1178
1179    vc->terminal = vte_terminal_new();
1180
1181    master_fd = qemu_openpty_raw(&slave_fd, NULL);
1182    g_assert(master_fd != -1);
1183
1184#if VTE_CHECK_VERSION(0, 26, 0)
1185    pty = vte_pty_new_foreign(master_fd, NULL);
1186    vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
1187#else
1188    vte_terminal_set_pty(VTE_TERMINAL(vc->terminal), master_fd);
1189#endif
1190
1191    vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
1192
1193    vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
1194
1195    scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
1196    gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
1197
1198    vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
1199
1200    vc->fd = slave_fd;
1201    vc->chr->opaque = vc;
1202    vc->scrolled_window = scrolled_window;
1203
1204    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
1205                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1206
1207    gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
1208    g_signal_connect(vc->menu_item, "activate",
1209                     G_CALLBACK(gd_menu_switch_vc), s);
1210
1211    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1212
1213    qemu_chr_be_generic_open(vc->chr);
1214    if (vc->chr->init) {
1215        vc->chr->init(vc->chr);
1216    }
1217
1218    chan = g_io_channel_unix_new(vc->fd);
1219    g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
1220
1221    return group;
1222}
1223
1224/** Window Creation **/
1225
1226static void gd_connect_signals(GtkDisplayState *s)
1227{
1228    g_signal_connect(s->show_tabs_item, "activate",
1229                     G_CALLBACK(gd_menu_show_tabs), s);
1230
1231    g_signal_connect(s->window, "key-press-event",
1232                     G_CALLBACK(gd_window_key_event), s);
1233    g_signal_connect(s->window, "delete-event",
1234                     G_CALLBACK(gd_window_close), s);
1235
1236#if GTK_CHECK_VERSION(3, 0, 0)
1237    g_signal_connect(s->drawing_area, "draw",
1238                     G_CALLBACK(gd_draw_event), s);
1239#else
1240    g_signal_connect(s->drawing_area, "expose-event",
1241                     G_CALLBACK(gd_expose_event), s);
1242#endif
1243    g_signal_connect(s->drawing_area, "motion-notify-event",
1244                     G_CALLBACK(gd_motion_event), s);
1245    g_signal_connect(s->drawing_area, "button-press-event",
1246                     G_CALLBACK(gd_button_event), s);
1247    g_signal_connect(s->drawing_area, "button-release-event",
1248                     G_CALLBACK(gd_button_event), s);
1249    g_signal_connect(s->drawing_area, "key-press-event",
1250                     G_CALLBACK(gd_key_event), s);
1251    g_signal_connect(s->drawing_area, "key-release-event",
1252                     G_CALLBACK(gd_key_event), s);
1253
1254    g_signal_connect(s->pause_item, "activate",
1255                     G_CALLBACK(gd_menu_pause), s);
1256    g_signal_connect(s->reset_item, "activate",
1257                     G_CALLBACK(gd_menu_reset), s);
1258    g_signal_connect(s->powerdown_item, "activate",
1259                     G_CALLBACK(gd_menu_powerdown), s);
1260    g_signal_connect(s->quit_item, "activate",
1261                     G_CALLBACK(gd_menu_quit), s);
1262    g_signal_connect(s->full_screen_item, "activate",
1263                     G_CALLBACK(gd_menu_full_screen), s);
1264    g_signal_connect(s->zoom_in_item, "activate",
1265                     G_CALLBACK(gd_menu_zoom_in), s);
1266    g_signal_connect(s->zoom_out_item, "activate",
1267                     G_CALLBACK(gd_menu_zoom_out), s);
1268    g_signal_connect(s->zoom_fixed_item, "activate",
1269                     G_CALLBACK(gd_menu_zoom_fixed), s);
1270    g_signal_connect(s->zoom_fit_item, "activate",
1271                     G_CALLBACK(gd_menu_zoom_fit), s);
1272    g_signal_connect(s->vga_item, "activate",
1273                     G_CALLBACK(gd_menu_switch_vc), s);
1274    g_signal_connect(s->grab_item, "activate",
1275                     G_CALLBACK(gd_menu_grab_input), s);
1276    g_signal_connect(s->notebook, "switch-page",
1277                     G_CALLBACK(gd_change_page), s);
1278    g_signal_connect(s->drawing_area, "enter-notify-event",
1279                     G_CALLBACK(gd_enter_event), s);
1280    g_signal_connect(s->drawing_area, "leave-notify-event",
1281                     G_CALLBACK(gd_leave_event), s);
1282    g_signal_connect(s->drawing_area, "focus-out-event",
1283                     G_CALLBACK(gd_focus_out_event), s);
1284}
1285
1286static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group)
1287{
1288    GtkWidget *machine_menu;
1289    GtkWidget *separator;
1290    GtkStockItem item;
1291
1292    machine_menu = gtk_menu_new();
1293    gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group);
1294
1295    s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1296    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
1297
1298    separator = gtk_separator_menu_item_new();
1299    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1300
1301    s->reset_item = gtk_image_menu_item_new_with_mnemonic(_("_Reset"));
1302    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
1303
1304    s->powerdown_item = gtk_image_menu_item_new_with_mnemonic(_("Power _Down"));
1305    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
1306
1307    separator = gtk_separator_menu_item_new();
1308    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1309
1310    s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
1311    gtk_stock_lookup(GTK_STOCK_QUIT, &item);
1312    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
1313                                 "<QEMU>/Machine/Quit");
1314    gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier);
1315    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1316
1317    return machine_menu;
1318}
1319
1320static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group)
1321{
1322    GSList *group = NULL;
1323    GtkWidget *view_menu;
1324    GtkWidget *separator;
1325    int i;
1326
1327    view_menu = gtk_menu_new();
1328    gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group);
1329
1330    s->full_screen_item =
1331        gtk_image_menu_item_new_from_stock(GTK_STOCK_FULLSCREEN, NULL);
1332    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item),
1333                                 "<QEMU>/View/Full Screen");
1334    gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1335    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
1336
1337    separator = gtk_separator_menu_item_new();
1338    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1339
1340    s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL);
1341    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1342                                 "<QEMU>/View/Zoom In");
1343    gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1344    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
1345
1346    s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL);
1347    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1348                                 "<QEMU>/View/Zoom Out");
1349    gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1350    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
1351
1352    s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL);
1353    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1354                                 "<QEMU>/View/Zoom Fixed");
1355    gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1356    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
1357
1358    s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1359    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
1360
1361    separator = gtk_separator_menu_item_new();
1362    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1363
1364    s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1365    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
1366
1367    s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
1368    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
1369                                 "<QEMU>/View/Grab Input");
1370    gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1371    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
1372
1373    separator = gtk_separator_menu_item_new();
1374    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1375
1376    s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
1377    group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
1378    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
1379                                 "<QEMU>/View/VGA");
1380    gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1381    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item);
1382
1383    for (i = 0; i < nb_vcs; i++) {
1384        VirtualConsole *vc = &s->vc[i];
1385
1386        group = gd_vc_init(s, vc, i, group, view_menu);
1387        s->nb_vcs++;
1388    }
1389
1390    separator = gtk_separator_menu_item_new();
1391    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1392
1393    s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1394    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
1395
1396    return view_menu;
1397}
1398
1399static void gd_create_menus(GtkDisplayState *s)
1400{
1401    GtkAccelGroup *accel_group;
1402
1403    accel_group = gtk_accel_group_new();
1404    s->machine_menu = gd_create_menu_machine(s, accel_group);
1405    s->view_menu = gd_create_menu_view(s, accel_group);
1406
1407    s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
1408    gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
1409                              s->machine_menu);
1410    gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
1411
1412    s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
1413    gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1414    gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1415
1416    g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
1417    gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
1418    s->accel_group = accel_group;
1419}
1420
1421static const DisplayChangeListenerOps dcl_ops = {
1422    .dpy_name          = "gtk",
1423    .dpy_gfx_update    = gd_update,
1424    .dpy_gfx_switch    = gd_switch,
1425    .dpy_refresh       = gd_refresh,
1426    .dpy_mouse_set     = gd_mouse_set,
1427    .dpy_cursor_define = gd_cursor_define,
1428};
1429
1430void gtk_display_init(DisplayState *ds)
1431{
1432    GtkDisplayState *s = g_malloc0(sizeof(*s));
1433    char *filename;
1434
1435    gtk_init(NULL, NULL);
1436
1437    s->dcl.ops = &dcl_ops;
1438    s->dcl.con = qemu_console_lookup_by_index(0);
1439
1440    s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1441#if GTK_CHECK_VERSION(3, 2, 0)
1442    s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1443#else
1444    s->vbox = gtk_vbox_new(FALSE, 0);
1445#endif
1446    s->notebook = gtk_notebook_new();
1447    s->drawing_area = gtk_drawing_area_new();
1448    s->menu_bar = gtk_menu_bar_new();
1449
1450    s->scale_x = 1.0;
1451    s->scale_y = 1.0;
1452    s->free_scale = FALSE;
1453
1454    setlocale(LC_ALL, "");
1455    bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1456    textdomain("qemu");
1457
1458    s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1459
1460    s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1461    qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1462    qemu_add_vm_change_state_handler(gd_change_runstate, s);
1463
1464    gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
1465
1466    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
1467    if (filename) {
1468        GError *error = NULL;
1469        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
1470        if (pixbuf) {
1471            gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
1472        } else {
1473            g_error_free(error);
1474        }
1475        g_free(filename);
1476    }
1477
1478    gd_create_menus(s);
1479
1480    gd_connect_signals(s);
1481
1482    gtk_widget_add_events(s->drawing_area,
1483                          GDK_POINTER_MOTION_MASK |
1484                          GDK_BUTTON_PRESS_MASK |
1485                          GDK_BUTTON_RELEASE_MASK |
1486                          GDK_BUTTON_MOTION_MASK |
1487                          GDK_ENTER_NOTIFY_MASK |
1488                          GDK_LEAVE_NOTIFY_MASK |
1489                          GDK_SCROLL_MASK |
1490                          GDK_KEY_PRESS_MASK);
1491    gtk_widget_set_double_buffered(s->drawing_area, FALSE);
1492    gtk_widget_set_can_focus(s->drawing_area, TRUE);
1493
1494    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1495    gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1496
1497    gd_update_caption(s);
1498
1499    gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1500    gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1501
1502    gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
1503
1504    gtk_widget_show_all(s->window);
1505
1506    register_displaychangelistener(&s->dcl);
1507
1508    global_state = s;
1509}
1510