qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

gtk.c (75542B)


      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 
    119 static const guint16 *keycode_map;
    120 static size_t keycode_maplen;
    121 
    122 struct VCChardev {
    123     Chardev parent;
    124     VirtualConsole *console;
    125     bool echo;
    126 };
    127 typedef struct VCChardev VCChardev;
    128 
    129 #define TYPE_CHARDEV_VC "chardev-vc"
    130 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
    131                          TYPE_CHARDEV_VC)
    132 
    133 bool gtk_use_gl_area;
    134 
    135 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
    136 static void gd_ungrab_pointer(GtkDisplayState *s);
    137 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
    138 static void gd_ungrab_keyboard(GtkDisplayState *s);
    139 
    140 /** Utility Functions **/
    141 
    142 static 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 
    157 static 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 
    172 static 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 
    180 static 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 
    185 static 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 
    190 static 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 
    212 static 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 
    260 static 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 
    327 void 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 
    339 static 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 
    354 static 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 
    365 static 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 
    374 static 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 
    386 static 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 
    436 static void gd_refresh(DisplayChangeListener *dcl)
    437 {
    438     graphic_hw_update(dcl->con);
    439 }
    440 
    441 static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
    442 {
    443     return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
    444 }
    445 
    446 static 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 
    467 static 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 
    490 static 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 
    553 static 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 
    566 static 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 
    579 static 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 
    587 void gd_hw_gl_flushed(void *vcon)
    588 {
    589     VirtualConsole *vc = vcon;
    590     QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
    591 
    592     qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL);
    593     close(dmabuf->fence_fd);
    594     dmabuf->fence_fd = -1;
    595     graphic_hw_gl_block(vc->gfx.dcl.con, false);
    596 }
    597 
    598 /** DisplayState Callbacks (opengl version) **/
    599 
    600 static const DisplayChangeListenerOps dcl_gl_area_ops = {
    601     .dpy_name             = "gtk-egl",
    602     .dpy_gfx_update       = gd_gl_area_update,
    603     .dpy_gfx_switch       = gd_gl_area_switch,
    604     .dpy_gfx_check_format = console_gl_check_format,
    605     .dpy_refresh          = gd_gl_area_refresh,
    606     .dpy_mouse_set        = gd_mouse_set,
    607     .dpy_cursor_define    = gd_cursor_define,
    608 
    609     .dpy_gl_scanout_texture  = gd_gl_area_scanout_texture,
    610     .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
    611     .dpy_gl_update           = gd_gl_area_scanout_flush,
    612     .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
    613     .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
    614     .dpy_has_dmabuf          = gd_has_dmabuf,
    615 };
    616 
    617 static bool
    618 gd_gl_area_is_compatible_dcl(DisplayGLCtx *dgc,
    619                              DisplayChangeListener *dcl)
    620 {
    621     return dcl->ops == &dcl_gl_area_ops;
    622 }
    623 
    624 static const DisplayGLCtxOps gl_area_ctx_ops = {
    625     .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
    626     .dpy_gl_ctx_create       = gd_gl_area_create_context,
    627     .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
    628     .dpy_gl_ctx_make_current = gd_gl_area_make_current,
    629 };
    630 
    631 #ifdef CONFIG_X11
    632 static const DisplayChangeListenerOps dcl_egl_ops = {
    633     .dpy_name             = "gtk-egl",
    634     .dpy_gfx_update       = gd_egl_update,
    635     .dpy_gfx_switch       = gd_egl_switch,
    636     .dpy_gfx_check_format = console_gl_check_format,
    637     .dpy_refresh          = gd_egl_refresh,
    638     .dpy_mouse_set        = gd_mouse_set,
    639     .dpy_cursor_define    = gd_cursor_define,
    640 
    641     .dpy_gl_scanout_disable  = gd_egl_scanout_disable,
    642     .dpy_gl_scanout_texture  = gd_egl_scanout_texture,
    643     .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
    644     .dpy_gl_cursor_dmabuf    = gd_egl_cursor_dmabuf,
    645     .dpy_gl_cursor_position  = gd_egl_cursor_position,
    646     .dpy_gl_update           = gd_egl_flush,
    647     .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
    648     .dpy_has_dmabuf          = gd_has_dmabuf,
    649 };
    650 
    651 static bool
    652 gd_egl_is_compatible_dcl(DisplayGLCtx *dgc,
    653                          DisplayChangeListener *dcl)
    654 {
    655     return dcl->ops == &dcl_egl_ops;
    656 }
    657 
    658 static const DisplayGLCtxOps egl_ctx_ops = {
    659     .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
    660     .dpy_gl_ctx_create       = gd_egl_create_context,
    661     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
    662     .dpy_gl_ctx_make_current = gd_egl_make_current,
    663 };
    664 #endif
    665 
    666 #endif /* CONFIG_OPENGL */
    667 
    668 /** QEMU Events **/
    669 
    670 static void gd_change_runstate(void *opaque, bool running, RunState state)
    671 {
    672     GtkDisplayState *s = opaque;
    673 
    674     gd_update_caption(s);
    675 }
    676 
    677 static void gd_mouse_mode_change(Notifier *notify, void *data)
    678 {
    679     GtkDisplayState *s;
    680     int i;
    681 
    682     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
    683     /* release the grab at switching to absolute mode */
    684     if (qemu_input_is_absolute() && s->ptr_owner) {
    685         if (!s->ptr_owner->window) {
    686             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
    687                                            FALSE);
    688         } else {
    689             gd_ungrab_pointer(s);
    690         }
    691     }
    692     for (i = 0; i < s->nb_vcs; i++) {
    693         VirtualConsole *vc = &s->vc[i];
    694         gd_update_cursor(vc);
    695     }
    696 }
    697 
    698 /** GTK Events **/
    699 
    700 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
    701                                 void *opaque)
    702 {
    703     GtkDisplayState *s = opaque;
    704     bool allow_close = true;
    705 
    706     if (s->opts->has_window_close && !s->opts->window_close) {
    707         allow_close = false;
    708     }
    709 
    710     if (allow_close) {
    711         qmp_quit(NULL);
    712     }
    713 
    714     return TRUE;
    715 }
    716 
    717 static void gd_set_ui_refresh_rate(VirtualConsole *vc, int refresh_rate)
    718 {
    719     QemuUIInfo info;
    720 
    721     info = *dpy_get_ui_info(vc->gfx.dcl.con);
    722     info.refresh_rate = refresh_rate;
    723     dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
    724 }
    725 
    726 static void gd_set_ui_size(VirtualConsole *vc, gint width, gint height)
    727 {
    728     QemuUIInfo info;
    729 
    730     info = *dpy_get_ui_info(vc->gfx.dcl.con);
    731     info.width = width;
    732     info.height = height;
    733     dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
    734 }
    735 
    736 #if defined(CONFIG_OPENGL)
    737 
    738 static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
    739                                 void *opaque)
    740 {
    741     VirtualConsole *vc = opaque;
    742 
    743     if (vc->gfx.gls) {
    744         gd_gl_area_draw(vc);
    745     }
    746     return TRUE;
    747 }
    748 
    749 static void gd_resize_event(GtkGLArea *area,
    750                             gint width, gint height, gpointer *opaque)
    751 {
    752     VirtualConsole *vc = (void *)opaque;
    753 
    754     gd_set_ui_size(vc, width, height);
    755 }
    756 
    757 #endif
    758 
    759 void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget)
    760 {
    761 #ifdef GDK_VERSION_3_22
    762     GdkWindow *win = gtk_widget_get_window(widget);
    763     int refresh_rate;
    764 
    765     if (win) {
    766         GdkDisplay *dpy = gtk_widget_get_display(widget);
    767         GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
    768         refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */
    769     } else {
    770         refresh_rate = 0;
    771     }
    772 
    773     gd_set_ui_refresh_rate(vc, refresh_rate);
    774 
    775     /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
    776     vc->gfx.dcl.update_interval = refresh_rate ?
    777         MIN(1000 * 1000 / refresh_rate, GUI_REFRESH_INTERVAL_DEFAULT) :
    778         GUI_REFRESH_INTERVAL_DEFAULT;
    779 #endif
    780 }
    781 
    782 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
    783 {
    784     VirtualConsole *vc = opaque;
    785     GtkDisplayState *s = vc->s;
    786     int mx, my;
    787     int ww, wh;
    788     int fbw, fbh;
    789 
    790 #if defined(CONFIG_OPENGL)
    791     if (vc->gfx.gls) {
    792         if (gtk_use_gl_area) {
    793             /* invoke render callback please */
    794             return FALSE;
    795         } else {
    796 #ifdef CONFIG_X11
    797             gd_egl_draw(vc);
    798             return TRUE;
    799 #else
    800             abort();
    801 #endif
    802         }
    803     }
    804 #endif
    805 
    806     if (!gtk_widget_get_realized(widget)) {
    807         return FALSE;
    808     }
    809     if (!vc->gfx.ds) {
    810         return FALSE;
    811     }
    812     if (!vc->gfx.surface) {
    813         return FALSE;
    814     }
    815 
    816     gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : s->window);
    817 
    818     fbw = surface_width(vc->gfx.ds);
    819     fbh = surface_height(vc->gfx.ds);
    820 
    821     ww = gdk_window_get_width(gtk_widget_get_window(widget));
    822     wh = gdk_window_get_height(gtk_widget_get_window(widget));
    823 
    824     if (s->full_screen) {
    825         vc->gfx.scale_x = (double)ww / fbw;
    826         vc->gfx.scale_y = (double)wh / fbh;
    827     } else if (s->free_scale) {
    828         double sx, sy;
    829 
    830         sx = (double)ww / fbw;
    831         sy = (double)wh / fbh;
    832 
    833         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
    834     }
    835 
    836     fbw *= vc->gfx.scale_x;
    837     fbh *= vc->gfx.scale_y;
    838 
    839     mx = my = 0;
    840     if (ww > fbw) {
    841         mx = (ww - fbw) / 2;
    842     }
    843     if (wh > fbh) {
    844         my = (wh - fbh) / 2;
    845     }
    846 
    847     cairo_rectangle(cr, 0, 0, ww, wh);
    848 
    849     /* Optionally cut out the inner area where the pixmap
    850        will be drawn. This avoids 'flashing' since we're
    851        not double-buffering. Note we're using the undocumented
    852        behaviour of drawing the rectangle from right to left
    853        to cut out the whole */
    854     cairo_rectangle(cr, mx + fbw, my,
    855                     -1 * fbw, fbh);
    856     cairo_fill(cr);
    857 
    858     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
    859     cairo_set_source_surface(cr, vc->gfx.surface,
    860                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
    861     cairo_paint(cr);
    862 
    863     return TRUE;
    864 }
    865 
    866 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
    867                                 void *opaque)
    868 {
    869     VirtualConsole *vc = opaque;
    870     GtkDisplayState *s = vc->s;
    871     GdkWindow *window;
    872     int x, y;
    873     int mx, my;
    874     int fbh, fbw;
    875     int ww, wh, ws;
    876 
    877     if (!vc->gfx.ds) {
    878         return TRUE;
    879     }
    880 
    881     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
    882     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
    883 
    884     window = gtk_widget_get_window(vc->gfx.drawing_area);
    885     ww = gdk_window_get_width(window);
    886     wh = gdk_window_get_height(window);
    887     ws = gdk_window_get_scale_factor(window);
    888 
    889     mx = my = 0;
    890     if (ww > fbw) {
    891         mx = (ww - fbw) / 2;
    892     }
    893     if (wh > fbh) {
    894         my = (wh - fbh) / 2;
    895     }
    896 
    897     x = (motion->x - mx) / vc->gfx.scale_x * ws;
    898     y = (motion->y - my) / vc->gfx.scale_y * ws;
    899 
    900     if (qemu_input_is_absolute()) {
    901         if (x < 0 || y < 0 ||
    902             x >= surface_width(vc->gfx.ds) ||
    903             y >= surface_height(vc->gfx.ds)) {
    904             return TRUE;
    905         }
    906         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
    907                              0, surface_width(vc->gfx.ds));
    908         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
    909                              0, surface_height(vc->gfx.ds));
    910         qemu_input_event_sync();
    911     } else if (s->last_set && s->ptr_owner == vc) {
    912         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
    913         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
    914         qemu_input_event_sync();
    915     }
    916     s->last_x = x;
    917     s->last_y = y;
    918     s->last_set = TRUE;
    919 
    920     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
    921         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
    922         GdkDisplay *dpy = gtk_widget_get_display(widget);
    923         GdkWindow *win = gtk_widget_get_window(widget);
    924         GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
    925         GdkRectangle geometry;
    926 
    927         int x = (int)motion->x_root;
    928         int y = (int)motion->y_root;
    929 
    930         gdk_monitor_get_geometry(monitor, &geometry);
    931 
    932         /* In relative mode check to see if client pointer hit
    933          * one of the monitor edges, and if so move it back to the
    934          * center of the monitor. This is important because the pointer
    935          * in the server doesn't correspond 1-for-1, and so
    936          * may still be only half way across the screen. Without
    937          * this warp, the server pointer would thus appear to hit
    938          * an invisible wall */
    939         if (x <= geometry.x || x - geometry.x >= geometry.width - 1 ||
    940             y <= geometry.y || y - geometry.y >= geometry.height - 1) {
    941             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
    942             x = geometry.x + geometry.width / 2;
    943             y = geometry.y + geometry.height / 2;
    944 
    945             gdk_device_warp(dev, screen, x, y);
    946             s->last_set = FALSE;
    947             return FALSE;
    948         }
    949     }
    950     return TRUE;
    951 }
    952 
    953 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
    954                                 void *opaque)
    955 {
    956     VirtualConsole *vc = opaque;
    957     GtkDisplayState *s = vc->s;
    958     InputButton btn;
    959 
    960     /* implicitly grab the input at the first click in the relative mode */
    961     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
    962         !qemu_input_is_absolute() && s->ptr_owner != vc) {
    963         if (!vc->window) {
    964             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
    965                                            TRUE);
    966         } else {
    967             gd_grab_pointer(vc, "relative-mode-click");
    968         }
    969         return TRUE;
    970     }
    971 
    972     if (button->button == 1) {
    973         btn = INPUT_BUTTON_LEFT;
    974     } else if (button->button == 2) {
    975         btn = INPUT_BUTTON_MIDDLE;
    976     } else if (button->button == 3) {
    977         btn = INPUT_BUTTON_RIGHT;
    978     } else if (button->button == 8) {
    979         btn = INPUT_BUTTON_SIDE;
    980     } else if (button->button == 9) {
    981         btn = INPUT_BUTTON_EXTRA;
    982     } else {
    983         return TRUE;
    984     }
    985 
    986     if (button->type == GDK_2BUTTON_PRESS || button->type == GDK_3BUTTON_PRESS) {
    987         return TRUE;
    988     }
    989 
    990     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
    991                          button->type == GDK_BUTTON_PRESS);
    992     qemu_input_event_sync();
    993     return TRUE;
    994 }
    995 
    996 static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
    997                                 void *opaque)
    998 {
    999     VirtualConsole *vc = opaque;
   1000     InputButton btn_vertical;
   1001     InputButton btn_horizontal;
   1002     bool has_vertical = false;
   1003     bool has_horizontal = false;
   1004 
   1005     if (scroll->direction == GDK_SCROLL_UP) {
   1006         btn_vertical = INPUT_BUTTON_WHEEL_UP;
   1007         has_vertical = true;
   1008     } else if (scroll->direction == GDK_SCROLL_DOWN) {
   1009         btn_vertical = INPUT_BUTTON_WHEEL_DOWN;
   1010         has_vertical = true;
   1011     } else if (scroll->direction == GDK_SCROLL_LEFT) {
   1012         btn_horizontal = INPUT_BUTTON_WHEEL_LEFT;
   1013         has_horizontal = true;
   1014     } else if (scroll->direction == GDK_SCROLL_RIGHT) {
   1015         btn_horizontal = INPUT_BUTTON_WHEEL_RIGHT;
   1016         has_horizontal = true;
   1017     } else if (scroll->direction == GDK_SCROLL_SMOOTH) {
   1018         gdouble delta_x, delta_y;
   1019         if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
   1020                                          &delta_x, &delta_y)) {
   1021             return TRUE;
   1022         }
   1023 
   1024         if (delta_y > 0) {
   1025             btn_vertical = INPUT_BUTTON_WHEEL_DOWN;
   1026             has_vertical = true;
   1027         } else if (delta_y < 0) {
   1028             btn_vertical = INPUT_BUTTON_WHEEL_UP;
   1029             has_vertical = true;
   1030         } else if (delta_x > 0) {
   1031             btn_horizontal = INPUT_BUTTON_WHEEL_RIGHT;
   1032             has_horizontal = true;
   1033         } else if (delta_x < 0) {
   1034             btn_horizontal = INPUT_BUTTON_WHEEL_LEFT;
   1035             has_horizontal = true;
   1036         } else {
   1037             return TRUE;
   1038         }
   1039     } else {
   1040         return TRUE;
   1041     }
   1042 
   1043     if (has_vertical) {
   1044         qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, true);
   1045         qemu_input_event_sync();
   1046         qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, false);
   1047         qemu_input_event_sync();
   1048     }
   1049 
   1050     if (has_horizontal) {
   1051         qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, true);
   1052         qemu_input_event_sync();
   1053         qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, false);
   1054         qemu_input_event_sync();
   1055     }
   1056 
   1057     return TRUE;
   1058 }
   1059 
   1060 
   1061 static const guint16 *gd_get_keymap(size_t *maplen)
   1062 {
   1063     GdkDisplay *dpy = gdk_display_get_default();
   1064 
   1065 #ifdef GDK_WINDOWING_X11
   1066     if (GDK_IS_X11_DISPLAY(dpy)) {
   1067         trace_gd_keymap_windowing("x11");
   1068         return qemu_xkeymap_mapping_table(
   1069             gdk_x11_display_get_xdisplay(dpy), maplen);
   1070     }
   1071 #endif
   1072 
   1073 #ifdef GDK_WINDOWING_WAYLAND
   1074     if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
   1075         trace_gd_keymap_windowing("wayland");
   1076         *maplen = qemu_input_map_xorgevdev_to_qcode_len;
   1077         return qemu_input_map_xorgevdev_to_qcode;
   1078     }
   1079 #endif
   1080 
   1081 #ifdef GDK_WINDOWING_WIN32
   1082     if (GDK_IS_WIN32_DISPLAY(dpy)) {
   1083         trace_gd_keymap_windowing("win32");
   1084         *maplen = qemu_input_map_atset1_to_qcode_len;
   1085         return qemu_input_map_atset1_to_qcode;
   1086     }
   1087 #endif
   1088 
   1089 #ifdef GDK_WINDOWING_QUARTZ
   1090     if (GDK_IS_QUARTZ_DISPLAY(dpy)) {
   1091         trace_gd_keymap_windowing("quartz");
   1092         *maplen = qemu_input_map_osx_to_qcode_len;
   1093         return qemu_input_map_osx_to_qcode;
   1094     }
   1095 #endif
   1096 
   1097 #ifdef GDK_WINDOWING_BROADWAY
   1098     if (GDK_IS_BROADWAY_DISPLAY(dpy)) {
   1099         trace_gd_keymap_windowing("broadway");
   1100         g_warning("experimental: using broadway, x11 virtual keysym\n"
   1101                   "mapping - with very limited support. See also\n"
   1102                   "https://bugzilla.gnome.org/show_bug.cgi?id=700105");
   1103         *maplen = qemu_input_map_x11_to_qcode_len;
   1104         return qemu_input_map_x11_to_qcode;
   1105     }
   1106 #endif
   1107 
   1108     g_warning("Unsupported GDK Windowing platform.\n"
   1109               "Disabling extended keycode tables.\n"
   1110               "Please report to qemu-devel@nongnu.org\n"
   1111               "including the following information:\n"
   1112               "\n"
   1113               "  - Operating system\n"
   1114               "  - GDK Windowing system build\n");
   1115     return NULL;
   1116 }
   1117 
   1118 
   1119 static int gd_map_keycode(int scancode)
   1120 {
   1121     if (!keycode_map) {
   1122         return 0;
   1123     }
   1124     if (scancode > keycode_maplen) {
   1125         return 0;
   1126     }
   1127 
   1128     return keycode_map[scancode];
   1129 }
   1130 
   1131 static int gd_get_keycode(GdkEventKey *key)
   1132 {
   1133 #ifdef G_OS_WIN32
   1134     int scancode = gdk_event_get_scancode((GdkEvent *)key);
   1135 
   1136     /* translate Windows native scancodes to atset1 keycodes */
   1137     switch (scancode & (KF_EXTENDED | 0xff)) {
   1138     case 0x145:     /* NUMLOCK */
   1139         return scancode & 0xff;
   1140     }
   1141 
   1142     return scancode & KF_EXTENDED ?
   1143         0xe000 | (scancode & 0xff) : scancode & 0xff;
   1144 
   1145 #else
   1146     return key->hardware_keycode;
   1147 #endif
   1148 }
   1149 
   1150 static gboolean gd_text_key_down(GtkWidget *widget,
   1151                                  GdkEventKey *key, void *opaque)
   1152 {
   1153     VirtualConsole *vc = opaque;
   1154     QemuConsole *con = vc->gfx.dcl.con;
   1155 
   1156     if (key->keyval == GDK_KEY_Delete) {
   1157         kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
   1158     } else if (key->length) {
   1159         kbd_put_string_console(con, key->string, key->length);
   1160     } else {
   1161         int qcode = gd_map_keycode(gd_get_keycode(key));
   1162         kbd_put_qcode_console(con, qcode, false);
   1163     }
   1164     return TRUE;
   1165 }
   1166 
   1167 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
   1168 {
   1169     VirtualConsole *vc = opaque;
   1170     int keycode, qcode;
   1171 
   1172 #ifdef G_OS_WIN32
   1173     /* on windows, we ought to ignore the reserved key event? */
   1174     if (key->hardware_keycode == 0xff)
   1175         return false;
   1176 
   1177     if (!vc->s->kbd_owner) {
   1178         if (key->hardware_keycode == VK_LWIN ||
   1179             key->hardware_keycode == VK_RWIN) {
   1180             return FALSE;
   1181         }
   1182     }
   1183 #endif
   1184 
   1185     if (key->keyval == GDK_KEY_Pause
   1186 #ifdef G_OS_WIN32
   1187         /* for some reason GDK does not fill keyval for VK_PAUSE
   1188          * See https://bugzilla.gnome.org/show_bug.cgi?id=769214
   1189          */
   1190         || key->hardware_keycode == VK_PAUSE
   1191 #endif
   1192         ) {
   1193         qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
   1194                              key->type == GDK_KEY_PRESS);
   1195         return TRUE;
   1196     }
   1197 
   1198     keycode = gd_get_keycode(key);
   1199     qcode = gd_map_keycode(keycode);
   1200 
   1201     trace_gd_key_event(vc->label, keycode, qcode,
   1202                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
   1203 
   1204     qkbd_state_key_event(vc->gfx.kbd, qcode,
   1205                          key->type == GDK_KEY_PRESS);
   1206 
   1207     return TRUE;
   1208 }
   1209 
   1210 static gboolean gd_grab_broken_event(GtkWidget *widget,
   1211                                      GdkEventGrabBroken *event, void *opaque)
   1212 {
   1213 #ifdef CONFIG_WIN32
   1214     /*
   1215      * On Windows the Ctrl-Alt-Del key combination can't be grabbed. This
   1216      * key combination leaves all three keys in a stuck condition. We use
   1217      * the grab-broken-event to release all keys.
   1218      */
   1219     if (event->keyboard) {
   1220         VirtualConsole *vc = opaque;
   1221         GtkDisplayState *s = vc->s;
   1222 
   1223         gtk_release_modifiers(s);
   1224     }
   1225 #endif
   1226     return TRUE;
   1227 }
   1228 
   1229 static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
   1230 {
   1231     if (event->type == GDK_MOTION_NOTIFY) {
   1232         return gd_motion_event(widget, &event->motion, opaque);
   1233     }
   1234     return FALSE;
   1235 }
   1236 
   1237 /** Window Menu Actions **/
   1238 
   1239 static void gd_menu_pause(GtkMenuItem *item, void *opaque)
   1240 {
   1241     GtkDisplayState *s = opaque;
   1242 
   1243     if (s->external_pause_update) {
   1244         return;
   1245     }
   1246     if (runstate_is_running()) {
   1247         qmp_stop(NULL);
   1248     } else {
   1249         qmp_cont(NULL);
   1250     }
   1251 }
   1252 
   1253 static void gd_menu_reset(GtkMenuItem *item, void *opaque)
   1254 {
   1255     qmp_system_reset(NULL);
   1256 }
   1257 
   1258 static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
   1259 {
   1260     qmp_system_powerdown(NULL);
   1261 }
   1262 
   1263 static void gd_menu_quit(GtkMenuItem *item, void *opaque)
   1264 {
   1265     qmp_quit(NULL);
   1266 }
   1267 
   1268 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
   1269 {
   1270     GtkDisplayState *s = opaque;
   1271     VirtualConsole *vc = gd_vc_find_by_menu(s);
   1272     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
   1273     gint page;
   1274 
   1275     gtk_release_modifiers(s);
   1276     if (vc) {
   1277         page = gtk_notebook_page_num(nb, vc->tab_item);
   1278         gtk_notebook_set_current_page(nb, page);
   1279         gtk_widget_grab_focus(vc->focus);
   1280     }
   1281 }
   1282 
   1283 static void gd_accel_switch_vc(void *opaque)
   1284 {
   1285     VirtualConsole *vc = opaque;
   1286 
   1287     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
   1288 }
   1289 
   1290 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
   1291 {
   1292     GtkDisplayState *s = opaque;
   1293     VirtualConsole *vc = gd_vc_find_current(s);
   1294 
   1295     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
   1296         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
   1297     } else {
   1298         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
   1299     }
   1300     gd_update_windowsize(vc);
   1301 }
   1302 
   1303 static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
   1304                                     void *opaque)
   1305 {
   1306     VirtualConsole *vc = opaque;
   1307     GtkDisplayState *s = vc->s;
   1308 
   1309     gtk_widget_set_sensitive(vc->menu_item, true);
   1310     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
   1311     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
   1312                                     vc->tab_item, vc->label);
   1313     gtk_widget_destroy(vc->window);
   1314     vc->window = NULL;
   1315 #if defined(CONFIG_OPENGL)
   1316     if (vc->gfx.esurface) {
   1317         eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
   1318         vc->gfx.esurface = NULL;
   1319     }
   1320     if (vc->gfx.ectx) {
   1321         eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
   1322         vc->gfx.ectx = NULL;
   1323     }
   1324 #endif
   1325     return TRUE;
   1326 }
   1327 
   1328 static gboolean gd_win_grab(void *opaque)
   1329 {
   1330     VirtualConsole *vc = opaque;
   1331 
   1332     fprintf(stderr, "%s: %s\n", __func__, vc->label);
   1333     if (vc->s->ptr_owner) {
   1334         gd_ungrab_pointer(vc->s);
   1335     } else {
   1336         gd_grab_pointer(vc, "user-request-detached-tab");
   1337     }
   1338     return TRUE;
   1339 }
   1340 
   1341 static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
   1342 {
   1343     GtkDisplayState *s = opaque;
   1344     VirtualConsole *vc = gd_vc_find_current(s);
   1345 
   1346     if (vc->type == GD_VC_GFX &&
   1347         qemu_console_is_graphic(vc->gfx.dcl.con)) {
   1348         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
   1349                                        FALSE);
   1350     }
   1351     if (!vc->window) {
   1352         gtk_widget_set_sensitive(vc->menu_item, false);
   1353         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   1354 #if defined(CONFIG_OPENGL)
   1355         if (vc->gfx.esurface) {
   1356             eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
   1357             vc->gfx.esurface = NULL;
   1358         }
   1359         if (vc->gfx.esurface) {
   1360             eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
   1361             vc->gfx.ectx = NULL;
   1362         }
   1363 #endif
   1364         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
   1365 
   1366         g_signal_connect(vc->window, "delete-event",
   1367                          G_CALLBACK(gd_tab_window_close), vc);
   1368         gtk_widget_show_all(vc->window);
   1369 
   1370         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
   1371             GtkAccelGroup *ag = gtk_accel_group_new();
   1372             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
   1373 
   1374             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
   1375                                                vc, NULL);
   1376             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
   1377         }
   1378 
   1379         gd_update_geometry_hints(vc);
   1380         gd_update_caption(s);
   1381     }
   1382 }
   1383 
   1384 static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
   1385 {
   1386     GtkDisplayState *s = opaque;
   1387     VirtualConsole *vc = gd_vc_find_current(s);
   1388 
   1389     if (s->full_screen) {
   1390         return;
   1391     }
   1392 
   1393     if (gtk_check_menu_item_get_active(
   1394                 GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
   1395         gtk_widget_show(s->menu_bar);
   1396     } else {
   1397         gtk_widget_hide(s->menu_bar);
   1398     }
   1399     gd_update_windowsize(vc);
   1400 }
   1401 
   1402 static void gd_accel_show_menubar(void *opaque)
   1403 {
   1404     GtkDisplayState *s = opaque;
   1405     gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item));
   1406 }
   1407 
   1408 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
   1409 {
   1410     GtkDisplayState *s = opaque;
   1411     VirtualConsole *vc = gd_vc_find_current(s);
   1412 
   1413     if (!s->full_screen) {
   1414         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
   1415         gtk_widget_hide(s->menu_bar);
   1416         if (vc->type == GD_VC_GFX) {
   1417             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
   1418         }
   1419         gtk_window_fullscreen(GTK_WINDOW(s->window));
   1420         s->full_screen = TRUE;
   1421     } else {
   1422         gtk_window_unfullscreen(GTK_WINDOW(s->window));
   1423         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
   1424         if (gtk_check_menu_item_get_active(
   1425                     GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
   1426             gtk_widget_show(s->menu_bar);
   1427         }
   1428         s->full_screen = FALSE;
   1429         if (vc->type == GD_VC_GFX) {
   1430             vc->gfx.scale_x = 1.0;
   1431             vc->gfx.scale_y = 1.0;
   1432             gd_update_windowsize(vc);
   1433         }
   1434     }
   1435 
   1436     gd_update_cursor(vc);
   1437 }
   1438 
   1439 static void gd_accel_full_screen(void *opaque)
   1440 {
   1441     GtkDisplayState *s = opaque;
   1442     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
   1443 }
   1444 
   1445 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
   1446 {
   1447     GtkDisplayState *s = opaque;
   1448     VirtualConsole *vc = gd_vc_find_current(s);
   1449 
   1450     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
   1451                                    FALSE);
   1452 
   1453     vc->gfx.scale_x += VC_SCALE_STEP;
   1454     vc->gfx.scale_y += VC_SCALE_STEP;
   1455 
   1456     gd_update_windowsize(vc);
   1457 }
   1458 
   1459 static void gd_accel_zoom_in(void *opaque)
   1460 {
   1461     GtkDisplayState *s = opaque;
   1462     gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item));
   1463 }
   1464 
   1465 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
   1466 {
   1467     GtkDisplayState *s = opaque;
   1468     VirtualConsole *vc = gd_vc_find_current(s);
   1469 
   1470     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
   1471                                    FALSE);
   1472 
   1473     vc->gfx.scale_x -= VC_SCALE_STEP;
   1474     vc->gfx.scale_y -= VC_SCALE_STEP;
   1475 
   1476     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
   1477     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
   1478 
   1479     gd_update_windowsize(vc);
   1480 }
   1481 
   1482 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
   1483 {
   1484     GtkDisplayState *s = opaque;
   1485     VirtualConsole *vc = gd_vc_find_current(s);
   1486 
   1487     vc->gfx.scale_x = 1.0;
   1488     vc->gfx.scale_y = 1.0;
   1489 
   1490     gd_update_windowsize(vc);
   1491 }
   1492 
   1493 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
   1494 {
   1495     GtkDisplayState *s = opaque;
   1496     VirtualConsole *vc = gd_vc_find_current(s);
   1497 
   1498     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
   1499         s->free_scale = TRUE;
   1500     } else {
   1501         s->free_scale = FALSE;
   1502         vc->gfx.scale_x = 1.0;
   1503         vc->gfx.scale_y = 1.0;
   1504     }
   1505 
   1506     gd_update_windowsize(vc);
   1507     gd_update_full_redraw(vc);
   1508 }
   1509 
   1510 static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
   1511 {
   1512     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
   1513     GdkSeat *seat = gdk_display_get_default_seat(display);
   1514     GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
   1515     GdkSeatCapabilities caps = 0;
   1516     GdkCursor *cursor = NULL;
   1517 
   1518     if (kbd) {
   1519         caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
   1520     }
   1521     if (ptr) {
   1522         caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
   1523         cursor = vc->s->null_cursor;
   1524     }
   1525 
   1526     if (caps) {
   1527         gdk_seat_grab(seat, window, caps, false, cursor,
   1528                       NULL, NULL, NULL);
   1529     } else {
   1530         gdk_seat_ungrab(seat);
   1531     }
   1532 }
   1533 
   1534 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
   1535 {
   1536     if (vc->s->kbd_owner) {
   1537         if (vc->s->kbd_owner == vc) {
   1538             return;
   1539         } else {
   1540             gd_ungrab_keyboard(vc->s);
   1541         }
   1542     }
   1543 
   1544     win32_kbd_set_grab(true);
   1545     gd_grab_update(vc, true, vc->s->ptr_owner == vc);
   1546     vc->s->kbd_owner = vc;
   1547     gd_update_caption(vc->s);
   1548     trace_gd_grab(vc->label, "kbd", reason);
   1549 }
   1550 
   1551 static void gd_ungrab_keyboard(GtkDisplayState *s)
   1552 {
   1553     VirtualConsole *vc = s->kbd_owner;
   1554 
   1555     if (vc == NULL) {
   1556         return;
   1557     }
   1558     s->kbd_owner = NULL;
   1559 
   1560     win32_kbd_set_grab(false);
   1561     gd_grab_update(vc, false, vc->s->ptr_owner == vc);
   1562     gd_update_caption(s);
   1563     trace_gd_ungrab(vc->label, "kbd");
   1564 }
   1565 
   1566 static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
   1567 {
   1568     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
   1569 
   1570     if (vc->s->ptr_owner) {
   1571         if (vc->s->ptr_owner == vc) {
   1572             return;
   1573         } else {
   1574             gd_ungrab_pointer(vc->s);
   1575         }
   1576     }
   1577 
   1578     gd_grab_update(vc, vc->s->kbd_owner == vc, true);
   1579     gdk_device_get_position(gd_get_pointer(display),
   1580                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
   1581     vc->s->ptr_owner = vc;
   1582     gd_update_caption(vc->s);
   1583     trace_gd_grab(vc->label, "ptr", reason);
   1584 }
   1585 
   1586 static void gd_ungrab_pointer(GtkDisplayState *s)
   1587 {
   1588     VirtualConsole *vc = s->ptr_owner;
   1589     GdkDisplay *display;
   1590 
   1591     if (vc == NULL) {
   1592         return;
   1593     }
   1594     s->ptr_owner = NULL;
   1595 
   1596     display = gtk_widget_get_display(vc->gfx.drawing_area);
   1597     gd_grab_update(vc, vc->s->kbd_owner == vc, false);
   1598     gdk_device_warp(gd_get_pointer(display),
   1599                     gtk_widget_get_screen(vc->gfx.drawing_area),
   1600                     vc->s->grab_x_root, vc->s->grab_y_root);
   1601     gd_update_caption(s);
   1602     trace_gd_ungrab(vc->label, "ptr");
   1603 }
   1604 
   1605 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
   1606 {
   1607     GtkDisplayState *s = opaque;
   1608     VirtualConsole *vc = gd_vc_find_current(s);
   1609 
   1610     if (gd_is_grab_active(s)) {
   1611         gd_grab_keyboard(vc, "user-request-main-window");
   1612         gd_grab_pointer(vc, "user-request-main-window");
   1613     } else {
   1614         gd_ungrab_keyboard(s);
   1615         gd_ungrab_pointer(s);
   1616     }
   1617 
   1618     gd_update_cursor(vc);
   1619 }
   1620 
   1621 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
   1622                            gpointer data)
   1623 {
   1624     GtkDisplayState *s = data;
   1625     VirtualConsole *vc;
   1626     gboolean on_vga;
   1627 
   1628     if (!gtk_widget_get_realized(s->notebook)) {
   1629         return;
   1630     }
   1631 
   1632     vc = gd_vc_find_by_page(s, arg2);
   1633     if (!vc) {
   1634         return;
   1635     }
   1636     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
   1637                                    TRUE);
   1638     on_vga = (vc->type == GD_VC_GFX &&
   1639               qemu_console_is_graphic(vc->gfx.dcl.con));
   1640     if (!on_vga) {
   1641         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
   1642                                        FALSE);
   1643     } else if (s->full_screen) {
   1644         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
   1645                                        TRUE);
   1646     }
   1647     gtk_widget_set_sensitive(s->grab_item, on_vga);
   1648 #ifdef CONFIG_VTE
   1649     gtk_widget_set_sensitive(s->copy_item, vc->type == GD_VC_VTE);
   1650 #endif
   1651 
   1652     gd_update_windowsize(vc);
   1653     gd_update_cursor(vc);
   1654 }
   1655 
   1656 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
   1657                                gpointer opaque)
   1658 {
   1659     VirtualConsole *vc = opaque;
   1660     GtkDisplayState *s = vc->s;
   1661 
   1662     if (gd_grab_on_hover(s)) {
   1663         gd_grab_keyboard(vc, "grab-on-hover");
   1664     }
   1665     return TRUE;
   1666 }
   1667 
   1668 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
   1669                                gpointer opaque)
   1670 {
   1671     VirtualConsole *vc = opaque;
   1672     GtkDisplayState *s = vc->s;
   1673 
   1674     if (gd_grab_on_hover(s)) {
   1675         gd_ungrab_keyboard(s);
   1676     }
   1677     return TRUE;
   1678 }
   1679 
   1680 static gboolean gd_focus_in_event(GtkWidget *widget,
   1681                                   GdkEventFocus *event, gpointer opaque)
   1682 {
   1683     VirtualConsole *vc = opaque;
   1684 
   1685     win32_kbd_set_window(gd_win32_get_hwnd(vc));
   1686     return TRUE;
   1687 }
   1688 
   1689 static gboolean gd_focus_out_event(GtkWidget *widget,
   1690                                    GdkEventFocus *event, gpointer opaque)
   1691 {
   1692     VirtualConsole *vc = opaque;
   1693     GtkDisplayState *s = vc->s;
   1694 
   1695     win32_kbd_set_window(NULL);
   1696     gtk_release_modifiers(s);
   1697     return TRUE;
   1698 }
   1699 
   1700 static gboolean gd_configure(GtkWidget *widget,
   1701                              GdkEventConfigure *cfg, gpointer opaque)
   1702 {
   1703     VirtualConsole *vc = opaque;
   1704 
   1705     gd_set_ui_size(vc, cfg->width, cfg->height);
   1706     return FALSE;
   1707 }
   1708 
   1709 /** Virtual Console Callbacks **/
   1710 
   1711 static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
   1712                                int idx, GSList *group, GtkWidget *view_menu)
   1713 {
   1714     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
   1715     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
   1716             HOTKEY_MODIFIERS, 0,
   1717             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
   1718     gtk_accel_label_set_accel(
   1719             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
   1720             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
   1721 
   1722     g_signal_connect(vc->menu_item, "activate",
   1723                      G_CALLBACK(gd_menu_switch_vc), s);
   1724     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
   1725 
   1726     return gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
   1727 }
   1728 
   1729 #if defined(CONFIG_VTE)
   1730 static void gd_menu_copy(GtkMenuItem *item, void *opaque)
   1731 {
   1732     GtkDisplayState *s = opaque;
   1733     VirtualConsole *vc = gd_vc_find_current(s);
   1734 
   1735 #if VTE_CHECK_VERSION(0, 50, 0)
   1736     vte_terminal_copy_clipboard_format(VTE_TERMINAL(vc->vte.terminal),
   1737                                        VTE_FORMAT_TEXT);
   1738 #else
   1739     vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
   1740 #endif
   1741 }
   1742 
   1743 static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
   1744 {
   1745     VirtualConsole *vc = opaque;
   1746 
   1747     if (gtk_adjustment_get_upper(adjustment) >
   1748         gtk_adjustment_get_page_size(adjustment)) {
   1749         gtk_widget_show(vc->vte.scrollbar);
   1750     } else {
   1751         gtk_widget_hide(vc->vte.scrollbar);
   1752     }
   1753 }
   1754 
   1755 static void gd_vc_send_chars(VirtualConsole *vc)
   1756 {
   1757     uint32_t len, avail;
   1758 
   1759     len = qemu_chr_be_can_write(vc->vte.chr);
   1760     avail = fifo8_num_used(&vc->vte.out_fifo);
   1761     while (len > 0 && avail > 0) {
   1762         const uint8_t *buf;
   1763         uint32_t size;
   1764 
   1765         buf = fifo8_pop_buf(&vc->vte.out_fifo, MIN(len, avail), &size);
   1766         qemu_chr_be_write(vc->vte.chr, buf, size);
   1767         len = qemu_chr_be_can_write(vc->vte.chr);
   1768         avail -= size;
   1769     }
   1770 }
   1771 
   1772 static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
   1773 {
   1774     VCChardev *vcd = VC_CHARDEV(chr);
   1775     VirtualConsole *vc = vcd->console;
   1776 
   1777     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
   1778     return len;
   1779 }
   1780 
   1781 static void gd_vc_chr_accept_input(Chardev *chr)
   1782 {
   1783     VCChardev *vcd = VC_CHARDEV(chr);
   1784     VirtualConsole *vc = vcd->console;
   1785 
   1786     gd_vc_send_chars(vc);
   1787 }
   1788 
   1789 static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
   1790 {
   1791     VCChardev *vcd = VC_CHARDEV(chr);
   1792     VirtualConsole *vc = vcd->console;
   1793 
   1794     if (vc) {
   1795         vc->vte.echo = echo;
   1796     } else {
   1797         vcd->echo = echo;
   1798     }
   1799 }
   1800 
   1801 static int nb_vcs;
   1802 static Chardev *vcs[MAX_VCS];
   1803 static void gd_vc_open(Chardev *chr,
   1804                        ChardevBackend *backend,
   1805                        bool *be_opened,
   1806                        Error **errp)
   1807 {
   1808     if (nb_vcs == MAX_VCS) {
   1809         error_setg(errp, "Maximum number of consoles reached");
   1810         return;
   1811     }
   1812 
   1813     vcs[nb_vcs++] = chr;
   1814 
   1815     /* console/chardev init sometimes completes elsewhere in a 2nd
   1816      * stage, so defer OPENED events until they are fully initialized
   1817      */
   1818     *be_opened = false;
   1819 }
   1820 
   1821 static void char_gd_vc_class_init(ObjectClass *oc, void *data)
   1822 {
   1823     ChardevClass *cc = CHARDEV_CLASS(oc);
   1824 
   1825     cc->parse = qemu_chr_parse_vc;
   1826     cc->open = gd_vc_open;
   1827     cc->chr_write = gd_vc_chr_write;
   1828     cc->chr_accept_input = gd_vc_chr_accept_input;
   1829     cc->chr_set_echo = gd_vc_chr_set_echo;
   1830 }
   1831 
   1832 static const TypeInfo char_gd_vc_type_info = {
   1833     .name = TYPE_CHARDEV_VC,
   1834     .parent = TYPE_CHARDEV,
   1835     .instance_size = sizeof(VCChardev),
   1836     .class_init = char_gd_vc_class_init,
   1837 };
   1838 
   1839 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
   1840                          gpointer user_data)
   1841 {
   1842     VirtualConsole *vc = user_data;
   1843     uint32_t free;
   1844 
   1845     if (vc->vte.echo) {
   1846         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
   1847         int i;
   1848         for (i = 0; i < size; i++) {
   1849             uint8_t c = text[i];
   1850             if (c >= 128 || isprint(c)) {
   1851                 /* 8-bit characters are considered printable.  */
   1852                 vte_terminal_feed(term, &text[i], 1);
   1853             } else if (c == '\r' || c == '\n') {
   1854                 vte_terminal_feed(term, "\r\n", 2);
   1855             } else {
   1856                 char ctrl[2] = { '^', 0};
   1857                 ctrl[1] = text[i] ^ 64;
   1858                 vte_terminal_feed(term, ctrl, 2);
   1859             }
   1860         }
   1861     }
   1862 
   1863     free = fifo8_num_free(&vc->vte.out_fifo);
   1864     fifo8_push_all(&vc->vte.out_fifo, (uint8_t *)text, MIN(free, size));
   1865     gd_vc_send_chars(vc);
   1866 
   1867     return TRUE;
   1868 }
   1869 
   1870 static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
   1871                               Chardev *chr, int idx,
   1872                               GSList *group, GtkWidget *view_menu)
   1873 {
   1874     char buffer[32];
   1875     GtkWidget *box;
   1876     GtkWidget *scrollbar;
   1877     GtkAdjustment *vadjustment;
   1878     VCChardev *vcd = VC_CHARDEV(chr);
   1879 
   1880     vc->s = s;
   1881     vc->vte.echo = vcd->echo;
   1882     vc->vte.chr = chr;
   1883     fifo8_create(&vc->vte.out_fifo, 4096);
   1884     vcd->console = vc;
   1885 
   1886     snprintf(buffer, sizeof(buffer), "vc%d", idx);
   1887     vc->label = g_strdup_printf("%s", vc->vte.chr->label
   1888                                 ? vc->vte.chr->label : buffer);
   1889     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
   1890 
   1891     vc->vte.terminal = vte_terminal_new();
   1892     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
   1893 
   1894     /* The documentation says that the default is UTF-8, but actually it is
   1895      * 7-bit ASCII at least in VTE 0.38. The function is deprecated since
   1896      * VTE 0.54 (only UTF-8 is supported now). */
   1897 #if !VTE_CHECK_VERSION(0, 54, 0)
   1898 #if VTE_CHECK_VERSION(0, 38, 0)
   1899     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
   1900 #else
   1901     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
   1902 #endif
   1903 #endif
   1904 
   1905     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
   1906     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
   1907                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
   1908 
   1909 #if VTE_CHECK_VERSION(0, 28, 0)
   1910     vadjustment = gtk_scrollable_get_vadjustment
   1911         (GTK_SCROLLABLE(vc->vte.terminal));
   1912 #else
   1913     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
   1914 #endif
   1915 
   1916     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
   1917     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
   1918 
   1919     gtk_box_pack_end(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
   1920     gtk_box_pack_end(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
   1921 
   1922     vc->vte.box = box;
   1923     vc->vte.scrollbar = scrollbar;
   1924 
   1925     g_signal_connect(vadjustment, "changed",
   1926                      G_CALLBACK(gd_vc_adjustment_changed), vc);
   1927 
   1928     vc->type = GD_VC_VTE;
   1929     vc->tab_item = box;
   1930     vc->focus = vc->vte.terminal;
   1931     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
   1932                              gtk_label_new(vc->label));
   1933 
   1934     qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED);
   1935 
   1936     return group;
   1937 }
   1938 
   1939 static void gd_vcs_init(GtkDisplayState *s, GSList *group,
   1940                         GtkWidget *view_menu)
   1941 {
   1942     int i;
   1943 
   1944     for (i = 0; i < nb_vcs; i++) {
   1945         VirtualConsole *vc = &s->vc[s->nb_vcs];
   1946         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
   1947         s->nb_vcs++;
   1948     }
   1949 }
   1950 #endif /* CONFIG_VTE */
   1951 
   1952 /** Window Creation **/
   1953 
   1954 static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
   1955 {
   1956     g_signal_connect(vc->gfx.drawing_area, "draw",
   1957                      G_CALLBACK(gd_draw_event), vc);
   1958 #if defined(CONFIG_OPENGL)
   1959     if (gtk_use_gl_area) {
   1960         /* wire up GtkGlArea events */
   1961         g_signal_connect(vc->gfx.drawing_area, "render",
   1962                          G_CALLBACK(gd_render_event), vc);
   1963         g_signal_connect(vc->gfx.drawing_area, "resize",
   1964                          G_CALLBACK(gd_resize_event), vc);
   1965     }
   1966 #endif
   1967     if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
   1968         g_signal_connect(vc->gfx.drawing_area, "event",
   1969                          G_CALLBACK(gd_event), vc);
   1970         g_signal_connect(vc->gfx.drawing_area, "button-press-event",
   1971                          G_CALLBACK(gd_button_event), vc);
   1972         g_signal_connect(vc->gfx.drawing_area, "button-release-event",
   1973                          G_CALLBACK(gd_button_event), vc);
   1974         g_signal_connect(vc->gfx.drawing_area, "scroll-event",
   1975                          G_CALLBACK(gd_scroll_event), vc);
   1976         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
   1977                          G_CALLBACK(gd_key_event), vc);
   1978         g_signal_connect(vc->gfx.drawing_area, "key-release-event",
   1979                          G_CALLBACK(gd_key_event), vc);
   1980 
   1981         g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
   1982                          G_CALLBACK(gd_enter_event), vc);
   1983         g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
   1984                          G_CALLBACK(gd_leave_event), vc);
   1985         g_signal_connect(vc->gfx.drawing_area, "focus-in-event",
   1986                          G_CALLBACK(gd_focus_in_event), vc);
   1987         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
   1988                          G_CALLBACK(gd_focus_out_event), vc);
   1989         g_signal_connect(vc->gfx.drawing_area, "configure-event",
   1990                          G_CALLBACK(gd_configure), vc);
   1991         g_signal_connect(vc->gfx.drawing_area, "grab-broken-event",
   1992                          G_CALLBACK(gd_grab_broken_event), vc);
   1993     } else {
   1994         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
   1995                          G_CALLBACK(gd_text_key_down), vc);
   1996     }
   1997 }
   1998 
   1999 static void gd_connect_signals(GtkDisplayState *s)
   2000 {
   2001     g_signal_connect(s->show_tabs_item, "activate",
   2002                      G_CALLBACK(gd_menu_show_tabs), s);
   2003     g_signal_connect(s->untabify_item, "activate",
   2004                      G_CALLBACK(gd_menu_untabify), s);
   2005     g_signal_connect(s->show_menubar_item, "activate",
   2006                      G_CALLBACK(gd_menu_show_menubar), s);
   2007 
   2008     g_signal_connect(s->window, "delete-event",
   2009                      G_CALLBACK(gd_window_close), s);
   2010 
   2011     g_signal_connect(s->pause_item, "activate",
   2012                      G_CALLBACK(gd_menu_pause), s);
   2013     g_signal_connect(s->reset_item, "activate",
   2014                      G_CALLBACK(gd_menu_reset), s);
   2015     g_signal_connect(s->powerdown_item, "activate",
   2016                      G_CALLBACK(gd_menu_powerdown), s);
   2017     g_signal_connect(s->quit_item, "activate",
   2018                      G_CALLBACK(gd_menu_quit), s);
   2019 #if defined(CONFIG_VTE)
   2020     g_signal_connect(s->copy_item, "activate",
   2021                      G_CALLBACK(gd_menu_copy), s);
   2022 #endif
   2023     g_signal_connect(s->full_screen_item, "activate",
   2024                      G_CALLBACK(gd_menu_full_screen), s);
   2025     g_signal_connect(s->zoom_in_item, "activate",
   2026                      G_CALLBACK(gd_menu_zoom_in), s);
   2027     g_signal_connect(s->zoom_out_item, "activate",
   2028                      G_CALLBACK(gd_menu_zoom_out), s);
   2029     g_signal_connect(s->zoom_fixed_item, "activate",
   2030                      G_CALLBACK(gd_menu_zoom_fixed), s);
   2031     g_signal_connect(s->zoom_fit_item, "activate",
   2032                      G_CALLBACK(gd_menu_zoom_fit), s);
   2033     g_signal_connect(s->grab_item, "activate",
   2034                      G_CALLBACK(gd_menu_grab_input), s);
   2035     g_signal_connect(s->notebook, "switch-page",
   2036                      G_CALLBACK(gd_change_page), s);
   2037 }
   2038 
   2039 static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
   2040 {
   2041     GtkWidget *machine_menu;
   2042     GtkWidget *separator;
   2043 
   2044     machine_menu = gtk_menu_new();
   2045     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
   2046 
   2047     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
   2048     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
   2049 
   2050     separator = gtk_separator_menu_item_new();
   2051     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
   2052 
   2053     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
   2054     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
   2055 
   2056     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
   2057     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
   2058 
   2059     separator = gtk_separator_menu_item_new();
   2060     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
   2061 
   2062     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
   2063     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
   2064                                  "<QEMU>/Machine/Quit");
   2065     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
   2066                             GDK_KEY_q, HOTKEY_MODIFIERS);
   2067     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
   2068 
   2069     return machine_menu;
   2070 }
   2071 
   2072 #if defined(CONFIG_OPENGL)
   2073 static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
   2074 {
   2075     gtk_gl_area_make_current(area);
   2076     qemu_egl_display = eglGetCurrentDisplay();
   2077     vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
   2078     if (!vc->gfx.has_dmabuf) {
   2079         error_report("GtkGLArea console lacks DMABUF support.");
   2080     }
   2081 }
   2082 #endif
   2083 
   2084 static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
   2085                               QemuConsole *con, int idx,
   2086                               GSList *group, GtkWidget *view_menu)
   2087 {
   2088     bool zoom_to_fit = false;
   2089 
   2090     vc->label = qemu_console_get_label(con);
   2091     vc->s = s;
   2092     vc->gfx.scale_x = 1.0;
   2093     vc->gfx.scale_y = 1.0;
   2094 
   2095 #if defined(CONFIG_OPENGL)
   2096     if (display_opengl) {
   2097         if (gtk_use_gl_area) {
   2098             vc->gfx.drawing_area = gtk_gl_area_new();
   2099             g_signal_connect(vc->gfx.drawing_area, "realize",
   2100                              G_CALLBACK(gl_area_realize), vc);
   2101             vc->gfx.dcl.ops = &dcl_gl_area_ops;
   2102             vc->gfx.dgc.ops = &gl_area_ctx_ops;
   2103         } else {
   2104 #ifdef CONFIG_X11
   2105             vc->gfx.drawing_area = gtk_drawing_area_new();
   2106             /*
   2107              * gtk_widget_set_double_buffered() was deprecated in 3.14.
   2108              * It is required for opengl rendering on X11 though.  A
   2109              * proper replacement (native opengl support) is only
   2110              * available in 3.16+.  Silence the warning if possible.
   2111              */
   2112 #pragma GCC diagnostic push
   2113 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
   2114             gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
   2115 #pragma GCC diagnostic pop
   2116             vc->gfx.dcl.ops = &dcl_egl_ops;
   2117             vc->gfx.dgc.ops = &egl_ctx_ops;
   2118             vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
   2119 #else
   2120             abort();
   2121 #endif
   2122         }
   2123     } else
   2124 #endif
   2125     {
   2126         vc->gfx.drawing_area = gtk_drawing_area_new();
   2127         vc->gfx.dcl.ops = &dcl_ops;
   2128     }
   2129 
   2130 
   2131     gtk_widget_add_events(vc->gfx.drawing_area,
   2132                           GDK_POINTER_MOTION_MASK |
   2133                           GDK_BUTTON_PRESS_MASK |
   2134                           GDK_BUTTON_RELEASE_MASK |
   2135                           GDK_BUTTON_MOTION_MASK |
   2136                           GDK_ENTER_NOTIFY_MASK |
   2137                           GDK_LEAVE_NOTIFY_MASK |
   2138                           GDK_SCROLL_MASK |
   2139                           GDK_SMOOTH_SCROLL_MASK |
   2140                           GDK_KEY_PRESS_MASK);
   2141     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
   2142 
   2143     vc->type = GD_VC_GFX;
   2144     vc->tab_item = vc->gfx.drawing_area;
   2145     vc->focus = vc->gfx.drawing_area;
   2146     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
   2147                              vc->tab_item, gtk_label_new(vc->label));
   2148 
   2149     vc->gfx.kbd = qkbd_state_init(con);
   2150     vc->gfx.dcl.con = con;
   2151 
   2152     if (display_opengl) {
   2153         qemu_console_set_display_gl_ctx(con, &vc->gfx.dgc);
   2154     }
   2155     register_displaychangelistener(&vc->gfx.dcl);
   2156 
   2157     gd_connect_vc_gfx_signals(vc);
   2158     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
   2159 
   2160     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
   2161         zoom_to_fit = true;
   2162     }
   2163     if (s->opts->u.gtk.has_zoom_to_fit) {
   2164         zoom_to_fit = s->opts->u.gtk.zoom_to_fit;
   2165     }
   2166     if (zoom_to_fit) {
   2167         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
   2168         s->free_scale = true;
   2169     }
   2170 
   2171     return group;
   2172 }
   2173 
   2174 static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts)
   2175 {
   2176     GSList *group = NULL;
   2177     GtkWidget *view_menu;
   2178     GtkWidget *separator;
   2179     QemuConsole *con;
   2180     int vc;
   2181 
   2182     view_menu = gtk_menu_new();
   2183     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
   2184 
   2185     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
   2186 
   2187 #if defined(CONFIG_VTE)
   2188     s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
   2189     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
   2190 #endif
   2191 
   2192     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
   2193             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
   2194     gtk_accel_label_set_accel(
   2195             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
   2196             GDK_KEY_f, HOTKEY_MODIFIERS);
   2197     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
   2198 
   2199     separator = gtk_separator_menu_item_new();
   2200     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
   2201 
   2202     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
   2203     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
   2204                                  "<QEMU>/View/Zoom In");
   2205     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
   2206                             HOTKEY_MODIFIERS);
   2207     gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0,
   2208             g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL));
   2209     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
   2210 
   2211     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
   2212     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
   2213                                  "<QEMU>/View/Zoom Out");
   2214     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
   2215                             HOTKEY_MODIFIERS);
   2216     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
   2217 
   2218     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
   2219     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
   2220                                  "<QEMU>/View/Zoom Fixed");
   2221     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
   2222                             HOTKEY_MODIFIERS);
   2223     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
   2224 
   2225     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
   2226     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
   2227 
   2228     separator = gtk_separator_menu_item_new();
   2229     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
   2230 
   2231     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
   2232     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
   2233 
   2234     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
   2235     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
   2236                                  "<QEMU>/View/Grab Input");
   2237     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
   2238                             HOTKEY_MODIFIERS);
   2239     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
   2240 
   2241     separator = gtk_separator_menu_item_new();
   2242     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
   2243 
   2244     /* gfx */
   2245     for (vc = 0;; vc++) {
   2246         con = qemu_console_lookup_by_index(vc);
   2247         if (!con) {
   2248             break;
   2249         }
   2250         group = gd_vc_gfx_init(s, &s->vc[vc], con,
   2251                                vc, group, view_menu);
   2252         s->nb_vcs++;
   2253     }
   2254 
   2255 #if defined(CONFIG_VTE)
   2256     /* vte */
   2257     gd_vcs_init(s, group, view_menu);
   2258 #endif
   2259 
   2260     separator = gtk_separator_menu_item_new();
   2261     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
   2262 
   2263     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
   2264     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
   2265 
   2266     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
   2267     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
   2268 
   2269     s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
   2270             _("Show Menubar"));
   2271     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
   2272                                    !opts->u.gtk.has_show_menubar ||
   2273                                    opts->u.gtk.show_menubar);
   2274     gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
   2275             g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
   2276     gtk_accel_label_set_accel(
   2277             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))),
   2278             GDK_KEY_m, HOTKEY_MODIFIERS);
   2279     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item);
   2280 
   2281     return view_menu;
   2282 }
   2283 
   2284 static void gd_create_menus(GtkDisplayState *s, DisplayOptions *opts)
   2285 {
   2286     GtkSettings *settings;
   2287 
   2288     s->accel_group = gtk_accel_group_new();
   2289     s->machine_menu = gd_create_menu_machine(s);
   2290     s->view_menu = gd_create_menu_view(s, opts);
   2291 
   2292     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
   2293     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
   2294                               s->machine_menu);
   2295     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
   2296 
   2297     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
   2298     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
   2299     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
   2300 
   2301     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
   2302     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
   2303 
   2304     /* Disable the default "F10" menu shortcut. */
   2305     settings = gtk_widget_get_settings(s->window);
   2306     g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL);
   2307 }
   2308 
   2309 
   2310 static gboolean gtkinit;
   2311 
   2312 static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
   2313 {
   2314     VirtualConsole *vc;
   2315 
   2316     GtkDisplayState *s = g_malloc0(sizeof(*s));
   2317     GdkDisplay *window_display;
   2318     GtkIconTheme *theme;
   2319     char *dir;
   2320 
   2321     if (!gtkinit) {
   2322         fprintf(stderr, "gtk initialization failed\n");
   2323         exit(1);
   2324     }
   2325     assert(opts->type == DISPLAY_TYPE_GTK);
   2326     s->opts = opts;
   2327 
   2328     theme = gtk_icon_theme_get_default();
   2329     dir = get_relocated_path(CONFIG_QEMU_ICONDIR);
   2330     gtk_icon_theme_prepend_search_path(theme, dir);
   2331     g_free(dir);
   2332     g_set_prgname("qemu");
   2333 
   2334     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   2335     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
   2336     s->notebook = gtk_notebook_new();
   2337     s->menu_bar = gtk_menu_bar_new();
   2338 
   2339     s->free_scale = FALSE;
   2340 
   2341     /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For
   2342      * LC_CTYPE, we need to make sure that non-ASCII characters are considered
   2343      * printable, but without changing any of the character classes to make
   2344      * sure that we don't accidentally break implicit assumptions.  */
   2345     setlocale(LC_MESSAGES, "");
   2346     setlocale(LC_CTYPE, "C.UTF-8");
   2347     dir = get_relocated_path(CONFIG_QEMU_LOCALEDIR);
   2348     bindtextdomain("qemu", dir);
   2349     g_free(dir);
   2350     bind_textdomain_codeset("qemu", "UTF-8");
   2351     textdomain("qemu");
   2352 
   2353     window_display = gtk_widget_get_display(s->window);
   2354     if (s->opts->has_show_cursor && s->opts->show_cursor) {
   2355         s->null_cursor = NULL; /* default pointer */
   2356     } else {
   2357         s->null_cursor = gdk_cursor_new_for_display(window_display,
   2358                                                     GDK_BLANK_CURSOR);
   2359     }
   2360 
   2361     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
   2362     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
   2363     qemu_add_vm_change_state_handler(gd_change_runstate, s);
   2364 
   2365     gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu");
   2366 
   2367     gd_create_menus(s, opts);
   2368 
   2369     gd_connect_signals(s);
   2370 
   2371     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
   2372     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
   2373 
   2374     gd_update_caption(s);
   2375 
   2376     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
   2377     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
   2378 
   2379     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
   2380 
   2381     gtk_widget_show_all(s->window);
   2382     if (opts->u.gtk.has_show_menubar &&
   2383         !opts->u.gtk.show_menubar) {
   2384         gtk_widget_hide(s->menu_bar);
   2385     }
   2386 
   2387     vc = gd_vc_find_current(s);
   2388     gtk_widget_set_sensitive(s->view_menu, vc != NULL);
   2389 #ifdef CONFIG_VTE
   2390     gtk_widget_set_sensitive(s->copy_item,
   2391                              vc && vc->type == GD_VC_VTE);
   2392 #endif
   2393 
   2394     if (opts->has_full_screen &&
   2395         opts->full_screen) {
   2396         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
   2397     }
   2398     if (opts->u.gtk.has_grab_on_hover &&
   2399         opts->u.gtk.grab_on_hover) {
   2400         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
   2401     }
   2402     if (opts->u.gtk.has_show_tabs &&
   2403         opts->u.gtk.show_tabs) {
   2404         gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item));
   2405     }
   2406 #ifdef CONFIG_GTK_CLIPBOARD
   2407     gd_clipboard_init(s);
   2408 #endif /* CONFIG_GTK_CLIPBOARD */
   2409 }
   2410 
   2411 static void early_gtk_display_init(DisplayOptions *opts)
   2412 {
   2413     /* The QEMU code relies on the assumption that it's always run in
   2414      * the C locale. Therefore it is not prepared to deal with
   2415      * operations that produce different results depending on the
   2416      * locale, such as printf's formatting of decimal numbers, and
   2417      * possibly others.
   2418      *
   2419      * Since GTK+ calls setlocale() by default -importing the locale
   2420      * settings from the environment- we must prevent it from doing so
   2421      * using gtk_disable_setlocale().
   2422      *
   2423      * QEMU's GTK+ UI, however, _does_ have translations for some of
   2424      * the menu items. As a trade-off between a functionally correct
   2425      * QEMU and a fully internationalized UI we support importing
   2426      * LC_MESSAGES from the environment (see the setlocale() call
   2427      * earlier in this file). This allows us to display translated
   2428      * messages leaving everything else untouched.
   2429      */
   2430     gtk_disable_setlocale();
   2431     gtkinit = gtk_init_check(NULL, NULL);
   2432     if (!gtkinit) {
   2433         /* don't exit yet, that'll break -help */
   2434         return;
   2435     }
   2436 
   2437     assert(opts->type == DISPLAY_TYPE_GTK);
   2438     if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
   2439 #if defined(CONFIG_OPENGL)
   2440 #if defined(GDK_WINDOWING_WAYLAND)
   2441         if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
   2442             gtk_use_gl_area = true;
   2443             gtk_gl_area_init();
   2444         } else
   2445 #endif
   2446         {
   2447 #ifdef CONFIG_X11
   2448             DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
   2449             gtk_egl_init(mode);
   2450 #endif
   2451         }
   2452 #endif
   2453     }
   2454 
   2455     keycode_map = gd_get_keymap(&keycode_maplen);
   2456 
   2457 #if defined(CONFIG_VTE)
   2458     type_register(&char_gd_vc_type_info);
   2459 #endif
   2460 }
   2461 
   2462 static QemuDisplay qemu_display_gtk = {
   2463     .type       = DISPLAY_TYPE_GTK,
   2464     .early_init = early_gtk_display_init,
   2465     .init       = gtk_display_init,
   2466 };
   2467 
   2468 static void register_gtk(void)
   2469 {
   2470     qemu_display_register(&qemu_display_gtk);
   2471 }
   2472 
   2473 type_init(register_gtk);
   2474 
   2475 #ifdef CONFIG_OPENGL
   2476 module_dep("ui-opengl");
   2477 #endif