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