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