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