cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

gtk.c (72159B)


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