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/osdep.h"
  38#include "qemu-common.h"
  39#include "qemu/cutils.h"
  40
  41#include "ui/console.h"
  42#include "ui/gtk.h"
  43
  44#include <glib/gi18n.h>
  45#include <locale.h>
  46#if defined(CONFIG_VTE)
  47#include <vte/vte.h>
  48#endif
  49#include <math.h>
  50
  51#include "trace.h"
  52#include "ui/input.h"
  53#include "sysemu/sysemu.h"
  54#include "qmp-commands.h"
  55#include "x_keymap.h"
  56#include "keymaps.h"
  57#include "sysemu/char.h"
  58#include "qom/object.h"
  59
  60#define MAX_VCS 10
  61#define VC_WINDOW_X_MIN  320
  62#define VC_WINDOW_Y_MIN  240
  63#define VC_TERM_X_MIN     80
  64#define VC_TERM_Y_MIN     25
  65#define VC_SCALE_MIN    0.25
  66#define VC_SCALE_STEP   0.25
  67
  68#if !defined(CONFIG_VTE)
  69# define VTE_CHECK_VERSION(a, b, c) 0
  70#endif
  71
  72#if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
  73/*
  74 * The gtk2 vte terminal widget seriously messes up the window resize
  75 * for some reason.  You basically can't make the qemu window smaller
  76 * any more because the toplevel window geoemtry hints are overridden.
  77 *
  78 * Workaround that by hiding all vte widgets, except the one in the
  79 * current tab.
  80 *
  81 * Luckily everything works smooth in gtk3.
  82 */
  83# define VTE_RESIZE_HACK 1
  84#endif
  85
  86#if !GTK_CHECK_VERSION(2, 20, 0)
  87#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
  88#endif
  89
  90#ifndef GDK_IS_X11_DISPLAY
  91#define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
  92#endif
  93#ifndef GDK_IS_WIN32_DISPLAY
  94#define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
  95#endif
  96
  97#ifndef GDK_KEY_0
  98#define GDK_KEY_0 GDK_0
  99#define GDK_KEY_1 GDK_1
 100#define GDK_KEY_2 GDK_2
 101#define GDK_KEY_f GDK_f
 102#define GDK_KEY_g GDK_g
 103#define GDK_KEY_q GDK_q
 104#define GDK_KEY_plus GDK_plus
 105#define GDK_KEY_minus GDK_minus
 106#define GDK_KEY_Pause GDK_Pause
 107#endif
 108
 109/* Some older mingw versions lack this constant or have
 110 * it conditionally defined */
 111#ifdef _WIN32
 112# ifndef MAPVK_VK_TO_VSC
 113#  define MAPVK_VK_TO_VSC 0
 114# endif
 115#endif
 116
 117
 118#define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
 119
 120static const int modifier_keycode[] = {
 121    /* shift, control, alt keys, meta keys, both left & right */
 122    0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
 123};
 124
 125struct GtkDisplayState {
 126    GtkWidget *window;
 127
 128    GtkWidget *menu_bar;
 129
 130    GtkAccelGroup *accel_group;
 131
 132    GtkWidget *machine_menu_item;
 133    GtkWidget *machine_menu;
 134    GtkWidget *pause_item;
 135    GtkWidget *reset_item;
 136    GtkWidget *powerdown_item;
 137    GtkWidget *quit_item;
 138
 139    GtkWidget *view_menu_item;
 140    GtkWidget *view_menu;
 141    GtkWidget *full_screen_item;
 142    GtkWidget *zoom_in_item;
 143    GtkWidget *zoom_out_item;
 144    GtkWidget *zoom_fixed_item;
 145    GtkWidget *zoom_fit_item;
 146    GtkWidget *grab_item;
 147    GtkWidget *grab_on_hover_item;
 148
 149    int nb_vcs;
 150    VirtualConsole vc[MAX_VCS];
 151
 152    GtkWidget *show_tabs_item;
 153    GtkWidget *untabify_item;
 154
 155    GtkWidget *vbox;
 156    GtkWidget *notebook;
 157    int button_mask;
 158    gboolean last_set;
 159    int last_x;
 160    int last_y;
 161    int grab_x_root;
 162    int grab_y_root;
 163    VirtualConsole *kbd_owner;
 164    VirtualConsole *ptr_owner;
 165
 166    gboolean full_screen;
 167
 168    GdkCursor *null_cursor;
 169    Notifier mouse_mode_notifier;
 170    gboolean free_scale;
 171
 172    bool external_pause_update;
 173
 174    bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
 175    bool has_evdev;
 176    bool ignore_keys;
 177};
 178
 179static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 180static void gd_ungrab_pointer(GtkDisplayState *s);
 181static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
 182static void gd_ungrab_keyboard(GtkDisplayState *s);
 183
 184/** Utility Functions **/
 185
 186static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
 187{
 188    VirtualConsole *vc;
 189    gint i;
 190
 191    for (i = 0; i < s->nb_vcs; i++) {
 192        vc = &s->vc[i];
 193        if (gtk_check_menu_item_get_active
 194            (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
 195            return vc;
 196        }
 197    }
 198    return NULL;
 199}
 200
 201static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
 202{
 203    VirtualConsole *vc;
 204    gint i, p;
 205
 206    for (i = 0; i < s->nb_vcs; i++) {
 207        vc = &s->vc[i];
 208        p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
 209        if (p == page) {
 210            return vc;
 211        }
 212    }
 213    return NULL;
 214}
 215
 216static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
 217{
 218    gint page;
 219
 220    page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
 221    return gd_vc_find_by_page(s, page);
 222}
 223
 224static bool gd_is_grab_active(GtkDisplayState *s)
 225{
 226    return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
 227}
 228
 229static bool gd_grab_on_hover(GtkDisplayState *s)
 230{
 231    return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
 232}
 233
 234static void gd_update_cursor(VirtualConsole *vc)
 235{
 236    GtkDisplayState *s = vc->s;
 237    GdkWindow *window;
 238
 239    if (vc->type != GD_VC_GFX ||
 240        !qemu_console_is_graphic(vc->gfx.dcl.con)) {
 241        return;
 242    }
 243
 244    if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
 245        return;
 246    }
 247
 248    window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
 249    if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
 250        gdk_window_set_cursor(window, s->null_cursor);
 251    } else {
 252        gdk_window_set_cursor(window, NULL);
 253    }
 254}
 255
 256static void gd_update_caption(GtkDisplayState *s)
 257{
 258    const char *status = "";
 259    gchar *prefix;
 260    gchar *title;
 261    const char *grab = "";
 262    bool is_paused = !runstate_is_running();
 263    int i;
 264
 265    if (qemu_name) {
 266        prefix = g_strdup_printf("QEMU (%s)", qemu_name);
 267    } else {
 268        prefix = g_strdup_printf("QEMU");
 269    }
 270
 271    if (s->ptr_owner != NULL &&
 272        s->ptr_owner->window == NULL) {
 273        grab = _(" - Press Ctrl+Alt+G to release grab");
 274    }
 275
 276    if (is_paused) {
 277        status = _(" [Paused]");
 278    }
 279    s->external_pause_update = true;
 280    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
 281                                   is_paused);
 282    s->external_pause_update = false;
 283
 284    title = g_strdup_printf("%s%s%s", prefix, status, grab);
 285    gtk_window_set_title(GTK_WINDOW(s->window), title);
 286    g_free(title);
 287
 288    for (i = 0; i < s->nb_vcs; i++) {
 289        VirtualConsole *vc = &s->vc[i];
 290
 291        if (!vc->window) {
 292            continue;
 293        }
 294        title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
 295                                vc == s->kbd_owner ? " +kbd" : "",
 296                                vc == s->ptr_owner ? " +ptr" : "");
 297        gtk_window_set_title(GTK_WINDOW(vc->window), title);
 298        g_free(title);
 299    }
 300
 301    g_free(prefix);
 302}
 303
 304static void gd_update_geometry_hints(VirtualConsole *vc)
 305{
 306    GtkDisplayState *s = vc->s;
 307    GdkWindowHints mask = 0;
 308    GdkGeometry geo = {};
 309    GtkWidget *geo_widget = NULL;
 310    GtkWindow *geo_window;
 311
 312    if (vc->type == GD_VC_GFX) {
 313        if (!vc->gfx.ds) {
 314            return;
 315        }
 316        if (s->free_scale) {
 317            geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
 318            geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
 319            mask |= GDK_HINT_MIN_SIZE;
 320        } else {
 321            geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
 322            geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 323            mask |= GDK_HINT_MIN_SIZE;
 324        }
 325        geo_widget = vc->gfx.drawing_area;
 326        gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
 327
 328#if defined(CONFIG_VTE)
 329    } else if (vc->type == GD_VC_VTE) {
 330        VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
 331        GtkBorder *ib;
 332
 333        geo.width_inc  = vte_terminal_get_char_width(term);
 334        geo.height_inc = vte_terminal_get_char_height(term);
 335        mask |= GDK_HINT_RESIZE_INC;
 336        geo.base_width  = geo.width_inc;
 337        geo.base_height = geo.height_inc;
 338        mask |= GDK_HINT_BASE_SIZE;
 339        geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
 340        geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
 341        mask |= GDK_HINT_MIN_SIZE;
 342        gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
 343        geo.base_width  += ib->left + ib->right;
 344        geo.base_height += ib->top + ib->bottom;
 345        geo.min_width   += ib->left + ib->right;
 346        geo.min_height  += ib->top + ib->bottom;
 347        geo_widget = vc->vte.terminal;
 348#endif
 349    }
 350
 351    geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
 352    gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
 353}
 354
 355void gd_update_windowsize(VirtualConsole *vc)
 356{
 357    GtkDisplayState *s = vc->s;
 358
 359    gd_update_geometry_hints(vc);
 360
 361    if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
 362        gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
 363                          VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
 364    }
 365}
 366
 367static void gd_update_full_redraw(VirtualConsole *vc)
 368{
 369    GtkWidget *area = vc->gfx.drawing_area;
 370    int ww, wh;
 371    gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
 372#if defined(CONFIG_GTK_GL)
 373    if (vc->gfx.gls) {
 374        gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
 375        return;
 376    }
 377#endif
 378    gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
 379}
 380
 381static void gtk_release_modifiers(GtkDisplayState *s)
 382{
 383    VirtualConsole *vc = gd_vc_find_current(s);
 384    int i, keycode;
 385
 386    if (vc->type != GD_VC_GFX ||
 387        !qemu_console_is_graphic(vc->gfx.dcl.con)) {
 388        return;
 389    }
 390    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
 391        keycode = modifier_keycode[i];
 392        if (!s->modifier_pressed[i]) {
 393            continue;
 394        }
 395        qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
 396        s->modifier_pressed[i] = false;
 397    }
 398}
 399
 400static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
 401                               GtkWidget *widget)
 402{
 403    g_object_ref(G_OBJECT(widget));
 404    gtk_container_remove(GTK_CONTAINER(from), widget);
 405    gtk_container_add(GTK_CONTAINER(to), widget);
 406    g_object_unref(G_OBJECT(widget));
 407}
 408
 409/** DisplayState Callbacks **/
 410
 411static void gd_update(DisplayChangeListener *dcl,
 412                      int x, int y, int w, int h)
 413{
 414    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 415    GdkWindow *win;
 416    int x1, x2, y1, y2;
 417    int mx, my;
 418    int fbw, fbh;
 419    int ww, wh;
 420
 421    trace_gd_update(vc->label, x, y, w, h);
 422
 423    if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
 424        return;
 425    }
 426
 427    if (vc->gfx.convert) {
 428        pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
 429                               NULL, vc->gfx.convert,
 430                               x, y, 0, 0, x, y, w, h);
 431    }
 432
 433    x1 = floor(x * vc->gfx.scale_x);
 434    y1 = floor(y * vc->gfx.scale_y);
 435
 436    x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
 437    y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
 438
 439    fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
 440    fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 441
 442    win = gtk_widget_get_window(vc->gfx.drawing_area);
 443    if (!win) {
 444        return;
 445    }
 446    gdk_drawable_get_size(win, &ww, &wh);
 447
 448    mx = my = 0;
 449    if (ww > fbw) {
 450        mx = (ww - fbw) / 2;
 451    }
 452    if (wh > fbh) {
 453        my = (wh - fbh) / 2;
 454    }
 455
 456    gtk_widget_queue_draw_area(vc->gfx.drawing_area,
 457                               mx + x1, my + y1, (x2 - x1), (y2 - y1));
 458}
 459
 460static void gd_refresh(DisplayChangeListener *dcl)
 461{
 462    graphic_hw_update(dcl->con);
 463}
 464
 465#if GTK_CHECK_VERSION(3, 0, 0)
 466static void gd_mouse_set(DisplayChangeListener *dcl,
 467                         int x, int y, int visible)
 468{
 469    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 470    GdkDisplay *dpy;
 471    GdkDeviceManager *mgr;
 472    gint x_root, y_root;
 473
 474    if (qemu_input_is_absolute()) {
 475        return;
 476    }
 477
 478    dpy = gtk_widget_get_display(vc->gfx.drawing_area);
 479    mgr = gdk_display_get_device_manager(dpy);
 480    gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
 481                               x, y, &x_root, &y_root);
 482    gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
 483                    gtk_widget_get_screen(vc->gfx.drawing_area),
 484                    x_root, y_root);
 485    vc->s->last_x = x;
 486    vc->s->last_y = y;
 487}
 488#else
 489static void gd_mouse_set(DisplayChangeListener *dcl,
 490                         int x, int y, int visible)
 491{
 492    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 493    gint x_root, y_root;
 494
 495    if (qemu_input_is_absolute()) {
 496        return;
 497    }
 498
 499    gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
 500                               x, y, &x_root, &y_root);
 501    gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
 502                             gtk_widget_get_screen(vc->gfx.drawing_area),
 503                             x_root, y_root);
 504}
 505#endif
 506
 507static void gd_cursor_define(DisplayChangeListener *dcl,
 508                             QEMUCursor *c)
 509{
 510    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 511    GdkPixbuf *pixbuf;
 512    GdkCursor *cursor;
 513
 514    if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
 515        return;
 516    }
 517
 518    pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
 519                                      GDK_COLORSPACE_RGB, true, 8,
 520                                      c->width, c->height, c->width * 4,
 521                                      NULL, NULL);
 522    cursor = gdk_cursor_new_from_pixbuf
 523        (gtk_widget_get_display(vc->gfx.drawing_area),
 524         pixbuf, c->hot_x, c->hot_y);
 525    gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
 526    g_object_unref(pixbuf);
 527#if !GTK_CHECK_VERSION(3, 0, 0)
 528    gdk_cursor_unref(cursor);
 529#else
 530    g_object_unref(cursor);
 531#endif
 532}
 533
 534static void gd_switch(DisplayChangeListener *dcl,
 535                      DisplaySurface *surface)
 536{
 537    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 538    bool resized = true;
 539
 540    trace_gd_switch(vc->label,
 541                    surface ? surface_width(surface)  : 0,
 542                    surface ? surface_height(surface) : 0);
 543
 544    if (vc->gfx.surface) {
 545        cairo_surface_destroy(vc->gfx.surface);
 546        vc->gfx.surface = NULL;
 547    }
 548    if (vc->gfx.convert) {
 549        pixman_image_unref(vc->gfx.convert);
 550        vc->gfx.convert = NULL;
 551    }
 552
 553    if (vc->gfx.ds && surface &&
 554        surface_width(vc->gfx.ds) == surface_width(surface) &&
 555        surface_height(vc->gfx.ds) == surface_height(surface)) {
 556        resized = false;
 557    }
 558    vc->gfx.ds = surface;
 559
 560    if (!surface) {
 561        return;
 562    }
 563
 564    if (surface->format == PIXMAN_x8r8g8b8) {
 565        /*
 566         * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
 567         *
 568         * No need to convert, use surface directly.  Should be the
 569         * common case as this is qemu_default_pixelformat(32) too.
 570         */
 571        vc->gfx.surface = cairo_image_surface_create_for_data
 572            (surface_data(surface),
 573             CAIRO_FORMAT_RGB24,
 574             surface_width(surface),
 575             surface_height(surface),
 576             surface_stride(surface));
 577    } else {
 578        /* Must convert surface, use pixman to do it. */
 579        vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
 580                                                   surface_width(surface),
 581                                                   surface_height(surface),
 582                                                   NULL, 0);
 583        vc->gfx.surface = cairo_image_surface_create_for_data
 584            ((void *)pixman_image_get_data(vc->gfx.convert),
 585             CAIRO_FORMAT_RGB24,
 586             pixman_image_get_width(vc->gfx.convert),
 587             pixman_image_get_height(vc->gfx.convert),
 588             pixman_image_get_stride(vc->gfx.convert));
 589        pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
 590                               NULL, vc->gfx.convert,
 591                               0, 0, 0, 0, 0, 0,
 592                               pixman_image_get_width(vc->gfx.convert),
 593                               pixman_image_get_height(vc->gfx.convert));
 594    }
 595
 596    if (resized) {
 597        gd_update_windowsize(vc);
 598    } else {
 599        gd_update_full_redraw(vc);
 600    }
 601}
 602
 603static const DisplayChangeListenerOps dcl_ops = {
 604    .dpy_name             = "gtk",
 605    .dpy_gfx_update       = gd_update,
 606    .dpy_gfx_switch       = gd_switch,
 607    .dpy_gfx_check_format = qemu_pixman_check_format,
 608    .dpy_refresh          = gd_refresh,
 609    .dpy_mouse_set        = gd_mouse_set,
 610    .dpy_cursor_define    = gd_cursor_define,
 611};
 612
 613
 614#if defined(CONFIG_OPENGL)
 615
 616/** DisplayState Callbacks (opengl version) **/
 617
 618#if defined(CONFIG_GTK_GL)
 619
 620static const DisplayChangeListenerOps dcl_gl_area_ops = {
 621    .dpy_name             = "gtk-egl",
 622    .dpy_gfx_update       = gd_gl_area_update,
 623    .dpy_gfx_switch       = gd_gl_area_switch,
 624    .dpy_gfx_check_format = console_gl_check_format,
 625    .dpy_refresh          = gd_gl_area_refresh,
 626    .dpy_mouse_set        = gd_mouse_set,
 627    .dpy_cursor_define    = gd_cursor_define,
 628
 629    .dpy_gl_ctx_create       = gd_gl_area_create_context,
 630    .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
 631    .dpy_gl_ctx_make_current = gd_gl_area_make_current,
 632    .dpy_gl_ctx_get_current  = gd_gl_area_get_current_context,
 633    .dpy_gl_scanout          = gd_gl_area_scanout,
 634    .dpy_gl_update           = gd_gl_area_scanout_flush,
 635};
 636
 637#else
 638
 639static const DisplayChangeListenerOps dcl_egl_ops = {
 640    .dpy_name             = "gtk-egl",
 641    .dpy_gfx_update       = gd_egl_update,
 642    .dpy_gfx_switch       = gd_egl_switch,
 643    .dpy_gfx_check_format = console_gl_check_format,
 644    .dpy_refresh          = gd_egl_refresh,
 645    .dpy_mouse_set        = gd_mouse_set,
 646    .dpy_cursor_define    = gd_cursor_define,
 647
 648    .dpy_gl_ctx_create       = gd_egl_create_context,
 649    .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
 650    .dpy_gl_ctx_make_current = gd_egl_make_current,
 651    .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
 652    .dpy_gl_scanout          = gd_egl_scanout,
 653    .dpy_gl_update           = gd_egl_scanout_flush,
 654};
 655
 656#endif /* CONFIG_GTK_GL */
 657#endif /* CONFIG_OPENGL */
 658
 659/** QEMU Events **/
 660
 661static void gd_change_runstate(void *opaque, int running, RunState state)
 662{
 663    GtkDisplayState *s = opaque;
 664
 665    gd_update_caption(s);
 666}
 667
 668static void gd_mouse_mode_change(Notifier *notify, void *data)
 669{
 670    GtkDisplayState *s;
 671    int i;
 672
 673    s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
 674    /* release the grab at switching to absolute mode */
 675    if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
 676        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
 677                                       FALSE);
 678    }
 679    for (i = 0; i < s->nb_vcs; i++) {
 680        VirtualConsole *vc = &s->vc[i];
 681        gd_update_cursor(vc);
 682    }
 683}
 684
 685/** GTK Events **/
 686
 687static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
 688                                void *opaque)
 689{
 690    GtkDisplayState *s = opaque;
 691    int i;
 692
 693    if (!no_quit) {
 694        for (i = 0; i < s->nb_vcs; i++) {
 695            if (s->vc[i].type != GD_VC_GFX) {
 696                continue;
 697            }
 698            unregister_displaychangelistener(&s->vc[i].gfx.dcl);
 699        }
 700        qmp_quit(NULL);
 701        return FALSE;
 702    }
 703
 704    return TRUE;
 705}
 706
 707static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
 708{
 709    QemuUIInfo info;
 710
 711    memset(&info, 0, sizeof(info));
 712    info.width = width;
 713    info.height = height;
 714    dpy_set_ui_info(vc->gfx.dcl.con, &info);
 715}
 716
 717#if defined(CONFIG_GTK_GL)
 718
 719static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
 720                                void *opaque)
 721{
 722    VirtualConsole *vc = opaque;
 723
 724    if (vc->gfx.gls) {
 725        gd_gl_area_draw(vc);
 726    }
 727    return TRUE;
 728}
 729
 730static void gd_resize_event(GtkGLArea *area,
 731                            gint width, gint height, gpointer *opaque)
 732{
 733    VirtualConsole *vc = (void *)opaque;
 734
 735    gd_set_ui_info(vc, width, height);
 736}
 737
 738#endif
 739
 740static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
 741{
 742    VirtualConsole *vc = opaque;
 743    GtkDisplayState *s = vc->s;
 744    int mx, my;
 745    int ww, wh;
 746    int fbw, fbh;
 747
 748#if defined(CONFIG_OPENGL)
 749    if (vc->gfx.gls) {
 750#if defined(CONFIG_GTK_GL)
 751        /* invoke render callback please */
 752        return FALSE;
 753#else
 754        gd_egl_draw(vc);
 755        return TRUE;
 756#endif
 757    }
 758#endif
 759
 760    if (!gtk_widget_get_realized(widget)) {
 761        return FALSE;
 762    }
 763    if (!vc->gfx.ds) {
 764        return FALSE;
 765    }
 766
 767    fbw = surface_width(vc->gfx.ds);
 768    fbh = surface_height(vc->gfx.ds);
 769
 770    gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
 771
 772    if (s->full_screen) {
 773        vc->gfx.scale_x = (double)ww / fbw;
 774        vc->gfx.scale_y = (double)wh / fbh;
 775    } else if (s->free_scale) {
 776        double sx, sy;
 777
 778        sx = (double)ww / fbw;
 779        sy = (double)wh / fbh;
 780
 781        vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
 782    }
 783
 784    fbw *= vc->gfx.scale_x;
 785    fbh *= vc->gfx.scale_y;
 786
 787    mx = my = 0;
 788    if (ww > fbw) {
 789        mx = (ww - fbw) / 2;
 790    }
 791    if (wh > fbh) {
 792        my = (wh - fbh) / 2;
 793    }
 794
 795    cairo_rectangle(cr, 0, 0, ww, wh);
 796
 797    /* Optionally cut out the inner area where the pixmap
 798       will be drawn. This avoids 'flashing' since we're
 799       not double-buffering. Note we're using the undocumented
 800       behaviour of drawing the rectangle from right to left
 801       to cut out the whole */
 802    cairo_rectangle(cr, mx + fbw, my,
 803                    -1 * fbw, fbh);
 804    cairo_fill(cr);
 805
 806    cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
 807    cairo_set_source_surface(cr, vc->gfx.surface,
 808                             mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
 809    cairo_paint(cr);
 810
 811    return TRUE;
 812}
 813
 814#if !GTK_CHECK_VERSION(3, 0, 0)
 815static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
 816                                void *opaque)
 817{
 818    cairo_t *cr;
 819    gboolean ret;
 820
 821    cr = gdk_cairo_create(gtk_widget_get_window(widget));
 822    cairo_rectangle(cr,
 823                    expose->area.x,
 824                    expose->area.y,
 825                    expose->area.width,
 826                    expose->area.height);
 827    cairo_clip(cr);
 828
 829    ret = gd_draw_event(widget, cr, opaque);
 830
 831    cairo_destroy(cr);
 832
 833    return ret;
 834}
 835#endif
 836
 837static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
 838                                void *opaque)
 839{
 840    VirtualConsole *vc = opaque;
 841    GtkDisplayState *s = vc->s;
 842    int x, y;
 843    int mx, my;
 844    int fbh, fbw;
 845    int ww, wh;
 846
 847    if (!vc->gfx.ds) {
 848        return TRUE;
 849    }
 850
 851    fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
 852    fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 853
 854    gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
 855                          &ww, &wh);
 856
 857    mx = my = 0;
 858    if (ww > fbw) {
 859        mx = (ww - fbw) / 2;
 860    }
 861    if (wh > fbh) {
 862        my = (wh - fbh) / 2;
 863    }
 864
 865    x = (motion->x - mx) / vc->gfx.scale_x;
 866    y = (motion->y - my) / vc->gfx.scale_y;
 867
 868    if (qemu_input_is_absolute()) {
 869        if (x < 0 || y < 0 ||
 870            x >= surface_width(vc->gfx.ds) ||
 871            y >= surface_height(vc->gfx.ds)) {
 872            return TRUE;
 873        }
 874        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
 875                             surface_width(vc->gfx.ds));
 876        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
 877                             surface_height(vc->gfx.ds));
 878        qemu_input_event_sync();
 879    } else if (s->last_set && s->ptr_owner == vc) {
 880        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
 881        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
 882        qemu_input_event_sync();
 883    }
 884    s->last_x = x;
 885    s->last_y = y;
 886    s->last_set = TRUE;
 887
 888    if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
 889        GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
 890        int x = (int)motion->x_root;
 891        int y = (int)motion->y_root;
 892
 893        /* In relative mode check to see if client pointer hit
 894         * one of the screen edges, and if so move it back by
 895         * 200 pixels. This is important because the pointer
 896         * in the server doesn't correspond 1-for-1, and so
 897         * may still be only half way across the screen. Without
 898         * this warp, the server pointer would thus appear to hit
 899         * an invisible wall */
 900        if (x == 0) {
 901            x += 200;
 902        }
 903        if (y == 0) {
 904            y += 200;
 905        }
 906        if (x == (gdk_screen_get_width(screen) - 1)) {
 907            x -= 200;
 908        }
 909        if (y == (gdk_screen_get_height(screen) - 1)) {
 910            y -= 200;
 911        }
 912
 913        if (x != (int)motion->x_root || y != (int)motion->y_root) {
 914#if GTK_CHECK_VERSION(3, 0, 0)
 915            GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
 916            gdk_device_warp(dev, screen, x, y);
 917#else
 918            GdkDisplay *display = gtk_widget_get_display(widget);
 919            gdk_display_warp_pointer(display, screen, x, y);
 920#endif
 921            s->last_set = FALSE;
 922            return FALSE;
 923        }
 924    }
 925    return TRUE;
 926}
 927
 928static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
 929                                void *opaque)
 930{
 931    VirtualConsole *vc = opaque;
 932    GtkDisplayState *s = vc->s;
 933    InputButton btn;
 934
 935    /* implicitly grab the input at the first click in the relative mode */
 936    if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
 937        !qemu_input_is_absolute() && s->ptr_owner != vc) {
 938        if (!vc->window) {
 939            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
 940                                           TRUE);
 941        } else {
 942            gd_grab_pointer(vc, "relative-mode-click");
 943        }
 944        return TRUE;
 945    }
 946
 947    if (button->button == 1) {
 948        btn = INPUT_BUTTON_LEFT;
 949    } else if (button->button == 2) {
 950        btn = INPUT_BUTTON_MIDDLE;
 951    } else if (button->button == 3) {
 952        btn = INPUT_BUTTON_RIGHT;
 953    } else {
 954        return TRUE;
 955    }
 956
 957    qemu_input_queue_btn(vc->gfx.dcl.con, btn,
 958                         button->type == GDK_BUTTON_PRESS);
 959    qemu_input_event_sync();
 960    return TRUE;
 961}
 962
 963static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
 964                                void *opaque)
 965{
 966    VirtualConsole *vc = opaque;
 967    InputButton btn;
 968
 969    if (scroll->direction == GDK_SCROLL_UP) {
 970        btn = INPUT_BUTTON_WHEEL_UP;
 971    } else if (scroll->direction == GDK_SCROLL_DOWN) {
 972        btn = INPUT_BUTTON_WHEEL_DOWN;
 973    } else {
 974        return TRUE;
 975    }
 976
 977    qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
 978    qemu_input_event_sync();
 979    qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
 980    qemu_input_event_sync();
 981    return TRUE;
 982}
 983
 984static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
 985{
 986    int qemu_keycode;
 987
 988#ifdef GDK_WINDOWING_WIN32
 989    if (GDK_IS_WIN32_DISPLAY(dpy)) {
 990        qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
 991        switch (qemu_keycode) {
 992        case 103:   /* alt gr */
 993            qemu_keycode = 56 | SCANCODE_GREY;
 994            break;
 995        }
 996        return qemu_keycode;
 997    }
 998#endif
 999
1000    if (gdk_keycode < 9) {
1001        qemu_keycode = 0;
1002    } else if (gdk_keycode < 97) {
1003        qemu_keycode = gdk_keycode - 8;
1004#ifdef GDK_WINDOWING_X11
1005    } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
1006        if (s->has_evdev) {
1007            qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
1008        } else {
1009            qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
1010        }
1011#endif
1012    } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
1013        qemu_keycode = 0x70;
1014    } else if (gdk_keycode == 211) { /* backslash */
1015        qemu_keycode = 0x73;
1016    } else {
1017        qemu_keycode = 0;
1018    }
1019
1020    return qemu_keycode;
1021}
1022
1023static gboolean gd_text_key_down(GtkWidget *widget,
1024                                 GdkEventKey *key, void *opaque)
1025{
1026    VirtualConsole *vc = opaque;
1027    QemuConsole *con = vc->gfx.dcl.con;
1028
1029    if (key->length) {
1030        kbd_put_string_console(con, key->string, key->length);
1031    } else {
1032        int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
1033                                 key->hardware_keycode);
1034        int qcode = qemu_input_key_number_to_qcode(num);
1035        kbd_put_qcode_console(con, qcode);
1036    }
1037    return TRUE;
1038}
1039
1040static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
1041{
1042    VirtualConsole *vc = opaque;
1043    GtkDisplayState *s = vc->s;
1044    int gdk_keycode = key->hardware_keycode;
1045    int qemu_keycode;
1046    int i;
1047
1048    if (s->ignore_keys) {
1049        s->ignore_keys = (key->type == GDK_KEY_PRESS);
1050        return TRUE;
1051    }
1052
1053    if (key->keyval == GDK_KEY_Pause) {
1054        qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
1055                                        key->type == GDK_KEY_PRESS);
1056        return TRUE;
1057    }
1058
1059    qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
1060                                  gdk_keycode);
1061
1062    trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
1063                       (key->type == GDK_KEY_PRESS) ? "down" : "up");
1064
1065    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
1066        if (qemu_keycode == modifier_keycode[i]) {
1067            s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
1068        }
1069    }
1070
1071    qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
1072                                     key->type == GDK_KEY_PRESS);
1073
1074    return TRUE;
1075}
1076
1077static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
1078{
1079    if (event->type == GDK_MOTION_NOTIFY) {
1080        return gd_motion_event(widget, &event->motion, opaque);
1081    }
1082    return FALSE;
1083}
1084
1085/** Window Menu Actions **/
1086
1087static void gd_menu_pause(GtkMenuItem *item, void *opaque)
1088{
1089    GtkDisplayState *s = opaque;
1090
1091    if (s->external_pause_update) {
1092        return;
1093    }
1094    if (runstate_is_running()) {
1095        qmp_stop(NULL);
1096    } else {
1097        qmp_cont(NULL);
1098    }
1099}
1100
1101static void gd_menu_reset(GtkMenuItem *item, void *opaque)
1102{
1103    qmp_system_reset(NULL);
1104}
1105
1106static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
1107{
1108    qmp_system_powerdown(NULL);
1109}
1110
1111static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1112{
1113    qmp_quit(NULL);
1114}
1115
1116static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1117{
1118    GtkDisplayState *s = opaque;
1119    VirtualConsole *vc = gd_vc_find_by_menu(s);
1120    GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1121    gint page;
1122
1123    gtk_release_modifiers(s);
1124    if (vc) {
1125        page = gtk_notebook_page_num(nb, vc->tab_item);
1126        gtk_notebook_set_current_page(nb, page);
1127        gtk_widget_grab_focus(vc->focus);
1128    }
1129    s->ignore_keys = false;
1130}
1131
1132static void gd_accel_switch_vc(void *opaque)
1133{
1134    VirtualConsole *vc = opaque;
1135
1136    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1137#if !GTK_CHECK_VERSION(3, 0, 0)
1138    /* GTK2 sends the accel key to the target console - ignore this until */
1139    vc->s->ignore_keys = true;
1140#endif
1141}
1142
1143static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1144{
1145    GtkDisplayState *s = opaque;
1146    VirtualConsole *vc = gd_vc_find_current(s);
1147
1148    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1149        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1150    } else {
1151        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1152    }
1153    gd_update_windowsize(vc);
1154}
1155
1156static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1157                                    void *opaque)
1158{
1159    VirtualConsole *vc = opaque;
1160    GtkDisplayState *s = vc->s;
1161
1162    gtk_widget_set_sensitive(vc->menu_item, true);
1163    gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1164    gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1165                                    vc->tab_item, vc->label);
1166    gtk_widget_destroy(vc->window);
1167    vc->window = NULL;
1168    return TRUE;
1169}
1170
1171static gboolean gd_win_grab(void *opaque)
1172{
1173    VirtualConsole *vc = opaque;
1174
1175    fprintf(stderr, "%s: %s\n", __func__, vc->label);
1176    if (vc->s->ptr_owner) {
1177        gd_ungrab_pointer(vc->s);
1178    } else {
1179        gd_grab_pointer(vc, "user-request-detached-tab");
1180    }
1181    return TRUE;
1182}
1183
1184static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1185{
1186    GtkDisplayState *s = opaque;
1187    VirtualConsole *vc = gd_vc_find_current(s);
1188
1189    if (vc->type == GD_VC_GFX &&
1190        qemu_console_is_graphic(vc->gfx.dcl.con)) {
1191        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1192                                       FALSE);
1193    }
1194    if (!vc->window) {
1195        gtk_widget_set_sensitive(vc->menu_item, false);
1196        vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1197        gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1198
1199        g_signal_connect(vc->window, "delete-event",
1200                         G_CALLBACK(gd_tab_window_close), vc);
1201        gtk_widget_show_all(vc->window);
1202
1203        if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1204            GtkAccelGroup *ag = gtk_accel_group_new();
1205            gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
1206
1207            GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1208                                               vc, NULL);
1209            gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1210        }
1211
1212        gd_update_geometry_hints(vc);
1213        gd_update_caption(s);
1214    }
1215}
1216
1217static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1218{
1219    GtkDisplayState *s = opaque;
1220    VirtualConsole *vc = gd_vc_find_current(s);
1221
1222    if (!s->full_screen) {
1223        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1224        gtk_widget_hide(s->menu_bar);
1225        if (vc->type == GD_VC_GFX) {
1226            gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1227        }
1228        gtk_window_fullscreen(GTK_WINDOW(s->window));
1229        s->full_screen = TRUE;
1230    } else {
1231        gtk_window_unfullscreen(GTK_WINDOW(s->window));
1232        gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1233        gtk_widget_show(s->menu_bar);
1234        s->full_screen = FALSE;
1235        if (vc->type == GD_VC_GFX) {
1236            vc->gfx.scale_x = 1.0;
1237            vc->gfx.scale_y = 1.0;
1238            gd_update_windowsize(vc);
1239        }
1240    }
1241
1242    gd_update_cursor(vc);
1243}
1244
1245static void gd_accel_full_screen(void *opaque)
1246{
1247    GtkDisplayState *s = opaque;
1248    gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
1249}
1250
1251static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1252{
1253    GtkDisplayState *s = opaque;
1254    VirtualConsole *vc = gd_vc_find_current(s);
1255
1256    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1257                                   FALSE);
1258
1259    vc->gfx.scale_x += VC_SCALE_STEP;
1260    vc->gfx.scale_y += VC_SCALE_STEP;
1261
1262    gd_update_windowsize(vc);
1263}
1264
1265static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1266{
1267    GtkDisplayState *s = opaque;
1268    VirtualConsole *vc = gd_vc_find_current(s);
1269
1270    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1271                                   FALSE);
1272
1273    vc->gfx.scale_x -= VC_SCALE_STEP;
1274    vc->gfx.scale_y -= VC_SCALE_STEP;
1275
1276    vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
1277    vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1278
1279    gd_update_windowsize(vc);
1280}
1281
1282static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1283{
1284    GtkDisplayState *s = opaque;
1285    VirtualConsole *vc = gd_vc_find_current(s);
1286
1287    vc->gfx.scale_x = 1.0;
1288    vc->gfx.scale_y = 1.0;
1289
1290    gd_update_windowsize(vc);
1291}
1292
1293static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1294{
1295    GtkDisplayState *s = opaque;
1296    VirtualConsole *vc = gd_vc_find_current(s);
1297
1298    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1299        s->free_scale = TRUE;
1300    } else {
1301        s->free_scale = FALSE;
1302        vc->gfx.scale_x = 1.0;
1303        vc->gfx.scale_y = 1.0;
1304    }
1305
1306    gd_update_windowsize(vc);
1307    gd_update_full_redraw(vc);
1308}
1309
1310#if GTK_CHECK_VERSION(3, 0, 0)
1311static void gd_grab_devices(VirtualConsole *vc, bool grab,
1312                            GdkInputSource source, GdkEventMask mask,
1313                            GdkCursor *cursor)
1314{
1315    GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1316    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1317    GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
1318    GList *tmp = devs;
1319
1320    for (tmp = devs; tmp; tmp = tmp->next) {
1321        GdkDevice *dev = tmp->data;
1322        if (gdk_device_get_source(dev) != source) {
1323            continue;
1324        }
1325        if (grab) {
1326            GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
1327            gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
1328                            mask, cursor, GDK_CURRENT_TIME);
1329        } else {
1330            gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1331        }
1332    }
1333    g_list_free(devs);
1334}
1335#endif
1336
1337static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
1338{
1339    if (vc->s->kbd_owner) {
1340        if (vc->s->kbd_owner == vc) {
1341            return;
1342        } else {
1343            gd_ungrab_keyboard(vc->s);
1344        }
1345    }
1346
1347#if GTK_CHECK_VERSION(3, 0, 0)
1348    gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
1349                   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1350                   NULL);
1351#else
1352    gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
1353                      FALSE,
1354                      GDK_CURRENT_TIME);
1355#endif
1356    vc->s->kbd_owner = vc;
1357    gd_update_caption(vc->s);
1358    trace_gd_grab(vc->label, "kbd", reason);
1359}
1360
1361static void gd_ungrab_keyboard(GtkDisplayState *s)
1362{
1363    VirtualConsole *vc = s->kbd_owner;
1364
1365    if (vc == NULL) {
1366        return;
1367    }
1368    s->kbd_owner = NULL;
1369
1370#if GTK_CHECK_VERSION(3, 0, 0)
1371    gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1372#else
1373    gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1374#endif
1375    gd_update_caption(s);
1376    trace_gd_ungrab(vc->label, "kbd");
1377}
1378
1379static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
1380{
1381    GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1382
1383    if (vc->s->ptr_owner) {
1384        if (vc->s->ptr_owner == vc) {
1385            return;
1386        } else {
1387            gd_ungrab_pointer(vc->s);
1388        }
1389    }
1390
1391#if GTK_CHECK_VERSION(3, 0, 0)
1392    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1393    gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
1394                    GDK_POINTER_MOTION_MASK |
1395                    GDK_BUTTON_PRESS_MASK |
1396                    GDK_BUTTON_RELEASE_MASK |
1397                    GDK_BUTTON_MOTION_MASK |
1398                    GDK_SCROLL_MASK,
1399                    vc->s->null_cursor);
1400    gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
1401                            NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
1402#else
1403    gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
1404                     FALSE, /* All events to come to our window directly */
1405                     GDK_POINTER_MOTION_MASK |
1406                     GDK_BUTTON_PRESS_MASK |
1407                     GDK_BUTTON_RELEASE_MASK |
1408                     GDK_BUTTON_MOTION_MASK |
1409                     GDK_SCROLL_MASK,
1410                     NULL, /* Allow cursor to move over entire desktop */
1411                     vc->s->null_cursor,
1412                     GDK_CURRENT_TIME);
1413    gdk_display_get_pointer(display, NULL,
1414                            &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
1415#endif
1416    vc->s->ptr_owner = vc;
1417    gd_update_caption(vc->s);
1418    trace_gd_grab(vc->label, "ptr", reason);
1419}
1420
1421static void gd_ungrab_pointer(GtkDisplayState *s)
1422{
1423    VirtualConsole *vc = s->ptr_owner;
1424
1425    if (vc == NULL) {
1426        return;
1427    }
1428    s->ptr_owner = NULL;
1429
1430    GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1431#if GTK_CHECK_VERSION(3, 0, 0)
1432    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1433    gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1434    gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
1435                    gtk_widget_get_screen(vc->gfx.drawing_area),
1436                    vc->s->grab_x_root, vc->s->grab_y_root);
1437#else
1438    gdk_pointer_ungrab(GDK_CURRENT_TIME);
1439    gdk_display_warp_pointer(display,
1440                             gtk_widget_get_screen(vc->gfx.drawing_area),
1441                             vc->s->grab_x_root, vc->s->grab_y_root);
1442#endif
1443    gd_update_caption(s);
1444    trace_gd_ungrab(vc->label, "ptr");
1445}
1446
1447static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
1448{
1449    GtkDisplayState *s = opaque;
1450    VirtualConsole *vc = gd_vc_find_current(s);
1451
1452    if (gd_is_grab_active(s)) {
1453        gd_grab_keyboard(vc, "user-request-main-window");
1454        gd_grab_pointer(vc, "user-request-main-window");
1455    } else {
1456        gd_ungrab_keyboard(s);
1457        gd_ungrab_pointer(s);
1458    }
1459
1460    gd_update_cursor(vc);
1461}
1462
1463static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1464                           gpointer data)
1465{
1466    GtkDisplayState *s = data;
1467    VirtualConsole *vc;
1468    gboolean on_vga;
1469
1470    if (!gtk_widget_get_realized(s->notebook)) {
1471        return;
1472    }
1473
1474#ifdef VTE_RESIZE_HACK
1475    vc = gd_vc_find_current(s);
1476    if (vc && vc->type == GD_VC_VTE) {
1477        gtk_widget_hide(vc->vte.terminal);
1478    }
1479#endif
1480    vc = gd_vc_find_by_page(s, arg2);
1481    if (!vc) {
1482        return;
1483    }
1484#ifdef VTE_RESIZE_HACK
1485    if (vc->type == GD_VC_VTE) {
1486        gtk_widget_show(vc->vte.terminal);
1487    }
1488#endif
1489    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1490                                   TRUE);
1491    on_vga = (vc->type == GD_VC_GFX &&
1492              qemu_console_is_graphic(vc->gfx.dcl.con));
1493    if (!on_vga) {
1494        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1495                                       FALSE);
1496    } else if (s->full_screen) {
1497        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1498                                       TRUE);
1499    }
1500    gtk_widget_set_sensitive(s->grab_item, on_vga);
1501
1502    gd_update_windowsize(vc);
1503    gd_update_cursor(vc);
1504}
1505
1506static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1507                               gpointer opaque)
1508{
1509    VirtualConsole *vc = opaque;
1510    GtkDisplayState *s = vc->s;
1511
1512    if (gd_grab_on_hover(s)) {
1513        gd_grab_keyboard(vc, "grab-on-hover");
1514    }
1515    return TRUE;
1516}
1517
1518static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1519                               gpointer opaque)
1520{
1521    VirtualConsole *vc = opaque;
1522    GtkDisplayState *s = vc->s;
1523
1524    if (gd_grab_on_hover(s)) {
1525        gd_ungrab_keyboard(s);
1526    }
1527    return TRUE;
1528}
1529
1530static gboolean gd_focus_out_event(GtkWidget *widget,
1531                                   GdkEventCrossing *crossing, gpointer opaque)
1532{
1533    VirtualConsole *vc = opaque;
1534    GtkDisplayState *s = vc->s;
1535
1536    gtk_release_modifiers(s);
1537    return TRUE;
1538}
1539
1540static gboolean gd_configure(GtkWidget *widget,
1541                             GdkEventConfigure *cfg, gpointer opaque)
1542{
1543    VirtualConsole *vc = opaque;
1544
1545    gd_set_ui_info(vc, cfg->width, cfg->height);
1546    return FALSE;
1547}
1548
1549/** Virtual Console Callbacks **/
1550
1551static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1552                               int idx, GSList *group, GtkWidget *view_menu)
1553{
1554    vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1555    gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1556            HOTKEY_MODIFIERS, 0,
1557            g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1558#if GTK_CHECK_VERSION(3, 8, 0)
1559    gtk_accel_label_set_accel(
1560            GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1561            GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1562#endif
1563
1564    g_signal_connect(vc->menu_item, "activate",
1565                     G_CALLBACK(gd_menu_switch_vc), s);
1566    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1567
1568    group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1569    return group;
1570}
1571
1572#if defined(CONFIG_VTE)
1573static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
1574{
1575    VirtualConsole *vc = opaque;
1576
1577    if (gtk_adjustment_get_upper(adjustment) >
1578        gtk_adjustment_get_page_size(adjustment)) {
1579        gtk_widget_show(vc->vte.scrollbar);
1580    } else {
1581        gtk_widget_hide(vc->vte.scrollbar);
1582    }
1583}
1584
1585static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1586{
1587    VirtualConsole *vc = chr->opaque;
1588
1589    vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1590    return len;
1591}
1592
1593static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
1594{
1595    VirtualConsole *vc = chr->opaque;
1596
1597    vc->vte.echo = echo;
1598}
1599
1600static int nb_vcs;
1601static CharDriverState *vcs[MAX_VCS];
1602
1603static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
1604{
1605    ChardevCommon *common = qapi_ChardevVC_base(vc);
1606    CharDriverState *chr;
1607
1608    chr = qemu_chr_alloc(common, errp);
1609    if (!chr) {
1610        return NULL;
1611    }
1612
1613    chr->chr_write = gd_vc_chr_write;
1614    chr->chr_set_echo = gd_vc_chr_set_echo;
1615
1616    /* Temporary, until gd_vc_vte_init runs.  */
1617    chr->opaque = g_new0(VirtualConsole, 1);
1618
1619    /* defer OPENED events until our vc is fully initialized */
1620    chr->explicit_be_open = true;
1621
1622    vcs[nb_vcs++] = chr;
1623
1624    return chr;
1625}
1626
1627static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1628                         gpointer user_data)
1629{
1630    VirtualConsole *vc = user_data;
1631
1632    if (vc->vte.echo) {
1633        VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
1634        int i;
1635        for (i = 0; i < size; i++) {
1636            uint8_t c = text[i];
1637            if (c >= 128 || isprint(c)) {
1638                /* 8-bit characters are considered printable.  */
1639                vte_terminal_feed(term, &text[i], 1);
1640            } else if (c == '\r' || c == '\n') {
1641                vte_terminal_feed(term, "\r\n", 2);
1642            } else {
1643                char ctrl[2] = { '^', 0};
1644                ctrl[1] = text[i] ^ 64;
1645                vte_terminal_feed(term, ctrl, 2);
1646            }
1647        }
1648    }
1649
1650    qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1651    return TRUE;
1652}
1653
1654static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1655                              CharDriverState *chr, int idx,
1656                              GSList *group, GtkWidget *view_menu)
1657{
1658    char buffer[32];
1659    GtkWidget *box;
1660    GtkWidget *scrollbar;
1661    GtkAdjustment *vadjustment;
1662    VirtualConsole *tmp_vc = chr->opaque;
1663
1664    vc->s = s;
1665    vc->vte.echo = tmp_vc->vte.echo;
1666
1667    vc->vte.chr = chr;
1668    chr->opaque = vc;
1669    g_free(tmp_vc);
1670
1671    snprintf(buffer, sizeof(buffer), "vc%d", idx);
1672    vc->label = g_strdup_printf("%s", vc->vte.chr->label
1673                                ? vc->vte.chr->label : buffer);
1674    group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1675
1676    vc->vte.terminal = vte_terminal_new();
1677    g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1678
1679    /* The documentation says that the default is UTF-8, but actually it is
1680     * 7-bit ASCII at least in VTE 0.38.
1681     */
1682#if VTE_CHECK_VERSION(0, 40, 0)
1683    vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
1684#else
1685    vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
1686#endif
1687
1688    vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
1689    vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
1690                          VC_TERM_X_MIN, VC_TERM_Y_MIN);
1691
1692#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
1693    vadjustment = gtk_scrollable_get_vadjustment
1694        (GTK_SCROLLABLE(vc->vte.terminal));
1695#else
1696    vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
1697#endif
1698
1699#if GTK_CHECK_VERSION(3, 0, 0)
1700    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1701    scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
1702#else
1703    box = gtk_hbox_new(false, 2);
1704    scrollbar = gtk_vscrollbar_new(vadjustment);
1705#endif
1706
1707    gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
1708    gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
1709
1710    vc->vte.box = box;
1711    vc->vte.scrollbar = scrollbar;
1712
1713    g_signal_connect(vadjustment, "changed",
1714                     G_CALLBACK(gd_vc_adjustment_changed), vc);
1715
1716    vc->type = GD_VC_VTE;
1717    vc->tab_item = box;
1718    vc->focus = vc->vte.terminal;
1719    gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1720                             gtk_label_new(vc->label));
1721
1722    qemu_chr_be_generic_open(vc->vte.chr);
1723    if (vc->vte.chr->init) {
1724        vc->vte.chr->init(vc->vte.chr);
1725    }
1726
1727    return group;
1728}
1729
1730static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1731                        GtkWidget *view_menu)
1732{
1733    int i;
1734
1735    for (i = 0; i < nb_vcs; i++) {
1736        VirtualConsole *vc = &s->vc[s->nb_vcs];
1737        group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1738        s->nb_vcs++;
1739    }
1740}
1741#endif /* CONFIG_VTE */
1742
1743/** Window Creation **/
1744
1745static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1746{
1747#if GTK_CHECK_VERSION(3, 0, 0)
1748    g_signal_connect(vc->gfx.drawing_area, "draw",
1749                     G_CALLBACK(gd_draw_event), vc);
1750#if defined(CONFIG_GTK_GL)
1751    if (display_opengl) {
1752        /* wire up GtkGlArea events */
1753        g_signal_connect(vc->gfx.drawing_area, "render",
1754                         G_CALLBACK(gd_render_event), vc);
1755        g_signal_connect(vc->gfx.drawing_area, "resize",
1756                         G_CALLBACK(gd_resize_event), vc);
1757    }
1758#endif
1759#else
1760    g_signal_connect(vc->gfx.drawing_area, "expose-event",
1761                     G_CALLBACK(gd_expose_event), vc);
1762#endif
1763    if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1764        g_signal_connect(vc->gfx.drawing_area, "event",
1765                         G_CALLBACK(gd_event), vc);
1766        g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1767                         G_CALLBACK(gd_button_event), vc);
1768        g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1769                         G_CALLBACK(gd_button_event), vc);
1770        g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1771                         G_CALLBACK(gd_scroll_event), vc);
1772        g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1773                         G_CALLBACK(gd_key_event), vc);
1774        g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1775                         G_CALLBACK(gd_key_event), vc);
1776
1777        g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1778                         G_CALLBACK(gd_enter_event), vc);
1779        g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1780                         G_CALLBACK(gd_leave_event), vc);
1781        g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1782                         G_CALLBACK(gd_focus_out_event), vc);
1783        g_signal_connect(vc->gfx.drawing_area, "configure-event",
1784                         G_CALLBACK(gd_configure), vc);
1785    } else {
1786        g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1787                         G_CALLBACK(gd_text_key_down), vc);
1788    }
1789}
1790
1791static void gd_connect_signals(GtkDisplayState *s)
1792{
1793    g_signal_connect(s->show_tabs_item, "activate",
1794                     G_CALLBACK(gd_menu_show_tabs), s);
1795    g_signal_connect(s->untabify_item, "activate",
1796                     G_CALLBACK(gd_menu_untabify), s);
1797
1798    g_signal_connect(s->window, "delete-event",
1799                     G_CALLBACK(gd_window_close), s);
1800
1801    g_signal_connect(s->pause_item, "activate",
1802                     G_CALLBACK(gd_menu_pause), s);
1803    g_signal_connect(s->reset_item, "activate",
1804                     G_CALLBACK(gd_menu_reset), s);
1805    g_signal_connect(s->powerdown_item, "activate",
1806                     G_CALLBACK(gd_menu_powerdown), s);
1807    g_signal_connect(s->quit_item, "activate",
1808                     G_CALLBACK(gd_menu_quit), s);
1809    g_signal_connect(s->full_screen_item, "activate",
1810                     G_CALLBACK(gd_menu_full_screen), s);
1811    g_signal_connect(s->zoom_in_item, "activate",
1812                     G_CALLBACK(gd_menu_zoom_in), s);
1813    g_signal_connect(s->zoom_out_item, "activate",
1814                     G_CALLBACK(gd_menu_zoom_out), s);
1815    g_signal_connect(s->zoom_fixed_item, "activate",
1816                     G_CALLBACK(gd_menu_zoom_fixed), s);
1817    g_signal_connect(s->zoom_fit_item, "activate",
1818                     G_CALLBACK(gd_menu_zoom_fit), s);
1819    g_signal_connect(s->grab_item, "activate",
1820                     G_CALLBACK(gd_menu_grab_input), s);
1821    g_signal_connect(s->notebook, "switch-page",
1822                     G_CALLBACK(gd_change_page), s);
1823}
1824
1825static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1826{
1827    GtkWidget *machine_menu;
1828    GtkWidget *separator;
1829
1830    machine_menu = gtk_menu_new();
1831    gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
1832
1833    s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1834    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
1835
1836    separator = gtk_separator_menu_item_new();
1837    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1838
1839    s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1840    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
1841
1842    s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1843    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
1844
1845    separator = gtk_separator_menu_item_new();
1846    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1847
1848    s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1849    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
1850                                 "<QEMU>/Machine/Quit");
1851    gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1852                            GDK_KEY_q, HOTKEY_MODIFIERS);
1853    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1854
1855    return machine_menu;
1856}
1857
1858static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1859                              QemuConsole *con, int idx,
1860                              GSList *group, GtkWidget *view_menu)
1861{
1862    vc->label = qemu_console_get_label(con);
1863    vc->s = s;
1864    vc->gfx.scale_x = 1.0;
1865    vc->gfx.scale_y = 1.0;
1866
1867#if defined(CONFIG_OPENGL)
1868    if (display_opengl) {
1869#if defined(CONFIG_GTK_GL)
1870        vc->gfx.drawing_area = gtk_gl_area_new();
1871        vc->gfx.dcl.ops = &dcl_gl_area_ops;
1872#else
1873        vc->gfx.drawing_area = gtk_drawing_area_new();
1874        /*
1875         * gtk_widget_set_double_buffered() was deprecated in 3.14.
1876         * It is required for opengl rendering on X11 though.  A
1877         * proper replacement (native opengl support) is only
1878         * available in 3.16+.  Silence the warning if possible.
1879         */
1880#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
1881#pragma GCC diagnostic push
1882#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1883#endif
1884        gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
1885#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
1886#pragma GCC diagnostic pop
1887#endif
1888        vc->gfx.dcl.ops = &dcl_egl_ops;
1889#endif /* CONFIG_GTK_GL */
1890    } else
1891#endif
1892    {
1893        vc->gfx.drawing_area = gtk_drawing_area_new();
1894        vc->gfx.dcl.ops = &dcl_ops;
1895    }
1896
1897
1898    gtk_widget_add_events(vc->gfx.drawing_area,
1899                          GDK_POINTER_MOTION_MASK |
1900                          GDK_BUTTON_PRESS_MASK |
1901                          GDK_BUTTON_RELEASE_MASK |
1902                          GDK_BUTTON_MOTION_MASK |
1903                          GDK_ENTER_NOTIFY_MASK |
1904                          GDK_LEAVE_NOTIFY_MASK |
1905                          GDK_SCROLL_MASK |
1906                          GDK_KEY_PRESS_MASK);
1907    gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
1908
1909    vc->type = GD_VC_GFX;
1910    vc->tab_item = vc->gfx.drawing_area;
1911    vc->focus = vc->gfx.drawing_area;
1912    gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
1913                             vc->tab_item, gtk_label_new(vc->label));
1914
1915    vc->gfx.dcl.con = con;
1916    register_displaychangelistener(&vc->gfx.dcl);
1917
1918    gd_connect_vc_gfx_signals(vc);
1919    group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1920
1921    if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
1922        gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
1923        s->free_scale = true;
1924    }
1925
1926    return group;
1927}
1928
1929static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
1930{
1931    GSList *group = NULL;
1932    GtkWidget *view_menu;
1933    GtkWidget *separator;
1934    QemuConsole *con;
1935    int vc;
1936
1937    view_menu = gtk_menu_new();
1938    gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
1939
1940    s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
1941
1942    gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
1943            g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
1944#if GTK_CHECK_VERSION(3, 8, 0)
1945    gtk_accel_label_set_accel(
1946            GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
1947            GDK_KEY_f, HOTKEY_MODIFIERS);
1948#endif
1949    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
1950
1951    separator = gtk_separator_menu_item_new();
1952    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1953
1954    s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
1955    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1956                                 "<QEMU>/View/Zoom In");
1957    gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
1958                            HOTKEY_MODIFIERS);
1959    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
1960
1961    s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
1962    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1963                                 "<QEMU>/View/Zoom Out");
1964    gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
1965                            HOTKEY_MODIFIERS);
1966    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
1967
1968    s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
1969    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1970                                 "<QEMU>/View/Zoom Fixed");
1971    gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
1972                            HOTKEY_MODIFIERS);
1973    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
1974
1975    s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1976    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
1977
1978    separator = gtk_separator_menu_item_new();
1979    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1980
1981    s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1982    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
1983
1984    s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
1985    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
1986                                 "<QEMU>/View/Grab Input");
1987    gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
1988                            HOTKEY_MODIFIERS);
1989    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
1990
1991    separator = gtk_separator_menu_item_new();
1992    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1993
1994    /* gfx */
1995    for (vc = 0;; vc++) {
1996        con = qemu_console_lookup_by_index(vc);
1997        if (!con) {
1998            break;
1999        }
2000        group = gd_vc_gfx_init(s, &s->vc[vc], con,
2001                               vc, group, view_menu);
2002        s->nb_vcs++;
2003    }
2004
2005#if defined(CONFIG_VTE)
2006    /* vte */
2007    gd_vcs_init(s, group, view_menu);
2008#endif
2009
2010    separator = gtk_separator_menu_item_new();
2011    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2012
2013    s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
2014    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
2015
2016    s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
2017    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
2018
2019    return view_menu;
2020}
2021
2022static void gd_create_menus(GtkDisplayState *s)
2023{
2024    s->accel_group = gtk_accel_group_new();
2025    s->machine_menu = gd_create_menu_machine(s);
2026    s->view_menu = gd_create_menu_view(s);
2027
2028    s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
2029    gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
2030                              s->machine_menu);
2031    gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
2032
2033    s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
2034    gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
2035    gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
2036
2037    g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
2038    gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
2039}
2040
2041static void gd_set_keycode_type(GtkDisplayState *s)
2042{
2043#ifdef GDK_WINDOWING_X11
2044    GdkDisplay *display = gtk_widget_get_display(s->window);
2045    if (GDK_IS_X11_DISPLAY(display)) {
2046        Display *x11_display = gdk_x11_display_get_xdisplay(display);
2047        XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
2048                                         XkbUseCoreKbd);
2049        char *keycodes = NULL;
2050
2051        if (desc && desc->names) {
2052            keycodes = XGetAtomName(x11_display, desc->names->keycodes);
2053        }
2054        if (keycodes == NULL) {
2055            fprintf(stderr, "could not lookup keycode name\n");
2056        } else if (strstart(keycodes, "evdev", NULL)) {
2057            s->has_evdev = true;
2058        } else if (!strstart(keycodes, "xfree86", NULL)) {
2059            fprintf(stderr, "unknown keycodes `%s', please report to "
2060                    "qemu-devel@nongnu.org\n", keycodes);
2061        }
2062
2063        if (desc) {
2064            XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
2065        }
2066        if (keycodes) {
2067            XFree(keycodes);
2068        }
2069    }
2070#endif
2071}
2072
2073static gboolean gtkinit;
2074
2075void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
2076{
2077    GtkDisplayState *s = g_malloc0(sizeof(*s));
2078    char *filename;
2079    GdkDisplay *window_display;
2080
2081    if (!gtkinit) {
2082        fprintf(stderr, "gtk initialization failed\n");
2083        exit(1);
2084    }
2085
2086    s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2087#if GTK_CHECK_VERSION(3, 2, 0)
2088    s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2089#else
2090    s->vbox = gtk_vbox_new(FALSE, 0);
2091#endif
2092    s->notebook = gtk_notebook_new();
2093    s->menu_bar = gtk_menu_bar_new();
2094
2095    s->free_scale = FALSE;
2096
2097    /* LC_MESSAGES only. See early_gtk_display_init() for details */
2098    setlocale(LC_MESSAGES, "");
2099    bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
2100    textdomain("qemu");
2101
2102    window_display = gtk_widget_get_display(s->window);
2103    s->null_cursor = gdk_cursor_new_for_display(window_display,
2104                                                GDK_BLANK_CURSOR);
2105
2106    s->mouse_mode_notifier.notify = gd_mouse_mode_change;
2107    qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
2108    qemu_add_vm_change_state_handler(gd_change_runstate, s);
2109
2110    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
2111    if (filename) {
2112        GError *error = NULL;
2113        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
2114        if (pixbuf) {
2115            gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
2116        } else {
2117            g_error_free(error);
2118        }
2119        g_free(filename);
2120    }
2121
2122    gd_create_menus(s);
2123
2124    gd_connect_signals(s);
2125
2126    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
2127    gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
2128
2129    gd_update_caption(s);
2130
2131    gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
2132    gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
2133
2134    gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2135
2136    gtk_widget_show_all(s->window);
2137
2138#ifdef VTE_RESIZE_HACK
2139    {
2140        VirtualConsole *cur = gd_vc_find_current(s);
2141        if (cur) {
2142            int i;
2143
2144            for (i = 0; i < s->nb_vcs; i++) {
2145                VirtualConsole *vc = &s->vc[i];
2146                if (vc && vc->type == GD_VC_VTE && vc != cur) {
2147                    gtk_widget_hide(vc->vte.terminal);
2148                }
2149            }
2150            gd_update_windowsize(cur);
2151        }
2152    }
2153#endif
2154
2155    if (full_screen) {
2156        gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2157    }
2158    if (grab_on_hover) {
2159        gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2160    }
2161
2162    gd_set_keycode_type(s);
2163}
2164
2165void early_gtk_display_init(int opengl)
2166{
2167    /* The QEMU code relies on the assumption that it's always run in
2168     * the C locale. Therefore it is not prepared to deal with
2169     * operations that produce different results depending on the
2170     * locale, such as printf's formatting of decimal numbers, and
2171     * possibly others.
2172     *
2173     * Since GTK+ calls setlocale() by default -importing the locale
2174     * settings from the environment- we must prevent it from doing so
2175     * using gtk_disable_setlocale().
2176     *
2177     * QEMU's GTK+ UI, however, _does_ have translations for some of
2178     * the menu items. As a trade-off between a functionally correct
2179     * QEMU and a fully internationalized UI we support importing
2180     * LC_MESSAGES from the environment (see the setlocale() call
2181     * earlier in this file). This allows us to display translated
2182     * messages leaving everything else untouched.
2183     */
2184    gtk_disable_setlocale();
2185    gtkinit = gtk_init_check(NULL, NULL);
2186    if (!gtkinit) {
2187        /* don't exit yet, that'll break -help */
2188        return;
2189    }
2190
2191    switch (opengl) {
2192    case -1: /* default */
2193    case 0:  /* off */
2194        break;
2195    case 1: /* on */
2196#if defined(CONFIG_OPENGL)
2197#if defined(CONFIG_GTK_GL)
2198        gtk_gl_area_init();
2199#else
2200        gtk_egl_init();
2201#endif
2202#endif
2203        break;
2204    default:
2205        g_assert_not_reached();
2206        break;
2207    }
2208
2209#if defined(CONFIG_VTE)
2210    register_vc_handler(gd_vc_handler);
2211#endif
2212}
2213