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